diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6c3a6b9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "gpu_transform/src/main/cpp/boundscheck/third_party_bounds_checking_function"] + path = gpu_transform/src/main/cpp/boundscheck/third_party_bounds_checking_function + url = https://gitee.com/openharmony/third_party_bounds_checking_function.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c5d7cc..f0a8d36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +## 3.1.1-rc.1 +- Photo reduction sampling +## 3.1.1-rc.0 +- 重构代码:抽取ImageKnifeDispatcher子线程requestJob相关代码到ImageKnifeLoader中,降低函数复杂度 +## 3.1.0 +- 部分静态webp图片有delay属性导致识别成动图,改用getFrameCount识别 +- 修复加载错误图后未去请求排队队列中的请求 +- 子线程本地Resource参数类型转换成number +- 修改使用hilog记录日志,默认打开debug级别的日志 +- file格式图片,fd同步close +- 解码pixelMap默认不可编辑,图形变化可编辑 +- 修改网络请求超时设置 + +## 3.1.0-rc.2 +- 修复宽高不等svg图片显示有毛边 + +## 3.1.0-rc.1 +- ImageKnifeAnimatorComponent新增开始、结束、暂停的回调事件 +- 文件缓存数量负数和超过INT最大值时默认为INT最大值 + +## 3.1.0-rc.0 +- ComponentV2装饰器适配 +- imageKnifeOption={...}用法改为new ImageKnifeOption({...}) +- animatorOption={...}用法改为new AnimatorOption({...}) + +## 3.0.1 +- 修复animatorOption属性设置初始化值失效 +- 网络请求code为206、204时返回arraybuffer +- ImageKnifeComponent显示非必要文件缓存初始化 +- 修复webp静态图无法设置图形变换 + ## 3.0.1-rc.2 - 修复自定义下载失败无失败回调 - 增加全局配置自定义下载接口 diff --git a/OAT.xml b/OAT.xml index 681e878..14adc5d 100644 --- a/OAT.xml +++ b/OAT.xml @@ -1,4 +1,21 @@ + 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 3f50768..23624dd 100644 --- a/README.md +++ b/README.md @@ -1,229 +1,228 @@ # ImageKnife -**专门为OpenHarmony打造的一款图像加载缓存库,致力于更高效、更轻便、更简单。** +ImageKnife is a specially crafted image loading and caching library for OpenHarmony, optimized for efficiency, lightness, and simplicity. -## 简介 +## Introduction -本项目参考开源库 [Glide](https://github.com/bumptech/glide) 进行OpenHarmony的自研版本: +This project is a self-developed version for OpenHarmony, inspired by the open-source [Glide](https://github.com/bumptech/glide) library. It sports the following features: -- 支持自定义内存缓存策略,支持设置内存缓存的大小(默认LRU策略)。 -- 支持磁盘二级缓存,对于下载图片会保存一份至磁盘当中。 -- 支持自定义实现图片获取/网络下载 -- 支持监听网络下载回调进度 -- 继承Image的能力,支持option传入border,设置边框,圆角 -- 继承Image的能力,支持option传入objectFit设置图片缩放,包括objectFit为auto时根据图片自适应高度 -- 支持通过设置transform缩放图片 -- 并发请求数量,支持请求排队队列的优先级 -- 支持生命周期已销毁的图片,不再发起请求 -- 自定义缓存key -- 自定义http网络请求头 -- 支持writeCacheStrategy控制缓存的存入策略(只存入内存或文件缓存) -- 支持preLoadCache预加载图片 -- 支持onlyRetrieveFromCache仅用缓存加载 -- 支持使用一个或多个图片变换,如模糊,高亮等 +- Customizable memory cache strategy with adjustable cache size (default LRU) +- Disk L2 cache for downloaded images +- Custom implementation for image acquisition and network downloading +- Listening for progress of network downloads through callbacks +- Image options for borders and rounded corners +- Image scaling with **objectFit**, including auto-adapting height +- Image scaling through transformation +- Concurrent request management with priority queuing +- No requests made for images whose lifecycle has been destroyed +- Custom cache keys +- Custom HTTP request headers +- **writeCacheStrategy** for controlling cache storage (memory or file) +- Preloading images with **preLoadCache** +- Loading images exclusively from cache with **onlyRetrieveFromCache** +- Support for image transformations such as blurring and highlighting -待实现特性 +Planned features: -- gif/webp动图显示与控制 -- 内存降采样优化,节约内存的占用 -- 支持自定义图片解码 +- Memory downsampling optimization to save memory usage +- Support for custom image decoding -注意:3.x版本相对2.x版本做了重大的重构,主要体现在: +Note: The 3.x version has been significantly restructured from the 2.x version, mainly in the following aspects: -- 使用Image组件代替Canvas组件渲染 -- 重构Dispatch分发逻辑,支持控制并发请求数,支持请求排队队列的优先级 -- 支持通过initMemoryCache自定义策略内存缓存策略和大小 -- 支持option自定义实现图片获取/网络下载 +- Use of the **Image** component instead of the **Canvas** component for rendering +- Refactored dispatch logic to control the number of concurrent requests and support priority in request queuing +- Support for custom memory cache strategies and sizes through **initMemoryCache** +- Support for custom implementation of image acquisition/network downloading through options -因此API及能力上,目前有部分差异,主要体现在: +Therefore, there are some differences in APIs and capabilities, which mainly include the following: -- 不支持drawLifeCycle接口,通过canvas自会图片 -- mainScaleType,border等参数,新版本与系统Image保持一致 -- gif/webp动图播放与控制 -- 抗锯齿相关参数 +- The **drawLifeCycle** API is not supported; images are drawn manually through the canvas. +- In the new version, parameters such as **mainScaleType** and **border** are consistent with the system **Image** component. +- GIF/WebP animation playback and control (implemented by **ImageAnimator**). +- Anti-aliasing related parameters. -## 下载安装 +## How to Install ``` ohpm install @ohos/imageknife -// 如果需要用文件缓存,需要提前初始化文件缓存 +// If file caching is required, initialize the file cache in advance. await ImageKnife.getInstance().initFileCache(context, 256, 256 * 1024 * 1024) ``` -## 使用说明 +## How to Use -#### 1.显示本地资源图片 +#### 1. Displaying a Local Resource Image ``` ImageKnifeComponent({ - ImageKnifeOption: { + ImageKnifeOption: new ImageKnifeOption({ loadSrc: $r("app.media.app_icon"), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto - } + }) }).width(100).height(100) ``` -#### 2.显示本地context files下文件 +#### 2. Displaying a File from Local Context Files ``` ImageKnifeComponent({ - ImageKnifeOption: { + ImageKnifeOption: new ImageKnifeOption({ loadSrc: this.localFile, placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto - } + }) }).width(100).height(100) ``` -#### 3.显示网络图片 +#### 3. Displaying a Network Image ``` ImageKnifeComponent({ - ImageKnifeOption: { + ImageKnifeOption: new ImageKnifeOption({ loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto - } + }) }).width(100).height(100) ``` -#### 4.自定义下载图片 +#### 4. Downloading an Image with Custom Options ``` ImageKnifeComponent({ - ImageKnifeOption: { + ImageKnifeOption: new ImageKnifeOption({ loadSrc: "https://file.atomgit.com/uploads/user/1704857786989_8994.jpeg", placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto, customGetImage: custom - } + }) }).width(100).height(100) -// 自定义实现图片获取方法,如自定义网络下载 +// Custom implementation of the image acquisition method, such as custom network download。 @Concurrent async function custom(context: Context, src: string | PixelMap | Resource): Promise { - console.info("ImageKnife:: custom download:" + src) - // 举例写死从本地文件读取,也可以自己请求网络图片 + console.info("ImageKnife:: custom download: " + src) + // Example of hardcoding to read from a local file. You can also request a network image. return context.resourceManager.getMediaContentSync($r("app.media.bb").id).buffer as ArrayBuffer } ``` -#### 5.监听网络下载进度 +#### 5. Listening for Network Download Progress ``` ImageKnifeComponent({ - ImageKnifeOption: { + ImageKnifeOption: new ImageKnifeOption({ loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", progressListener:(progress:number)=>{console.info("ImageKinfe:: call back progress = " + progress)} - } + }) }).width(100).height(100) ``` -#### 6.支持option传入border,设置边框,圆角 +#### 6. Setting Border Options ``` -ImageKnifeComponent({ ImageKnifeOption: +ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( { loadSrc: $r("app.media.rabbit"), border: {radius:50} - } + }) }).width(100).height(100) ``` -#### 7.支持option图片变换 +#### 7. Setting Image Transformation Options ``` -ImageKnifeComponent({ ImageKnifeOption: +ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( { loadSrc: $r("app.media.rabbit"), border: {radius:50}, transformation: new BlurTransformation(3) - } + }) }).width(100).height(100) ``` -多种组合变换用法 +Multiple combined transformation usages: ``` let transformations: collections.Array = new collections.Array(); transformations.push(new BlurTransformation(5)); transformations.push(new BrightnessTransformation(0.2)); ImageKnifeComponent({ - { + imageKnifeOption: new ImageKnifeOption({ loadSrc: $r('app.media.pngSample'), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Contain, - border: { radius: { topLeft: 50, bottomRight: 50 } }, // 圆角设置 - transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined // 图形变换组 -} + border: { radius: { topLeft: 50, bottomRight: 50 } }, // Rounded corner settings + transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined // Graphic transformation group +}) }).width(300) .height(300) - .rotate({ angle: 90 }) // 旋转90度 - .contrast(12) // 对比度滤波器 + .rotate ({angle: 90}) // Rotate by 90 degrees. + .contrast(12) // Contrast filter ``` -其他变换相关属性,可叠加实现组合变换效果 +Other transformation-related properties can be stacked to achieve combined transformation effects. -圆形裁剪变换示例 +Example of circular cropping transformation: ``` -ImageKnifeComponent({ ImageKnifeOption: +ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( { loadSrc: $r('app.media.pngSample'), objectFit: ImageFit.Cover, border: { radius: 150 } -} +}) }).width(300) .height(300) ``` -圆形裁剪带边框变换示例 +Example of Circular cropping with border transformation: ``` -ImageKnifeComponent({ ImageKnifeOption: +ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( { loadSrc: $r('app.media.pngSample'), objectFit: ImageFit.Cover, border: { radius: 150, color: Color.Red, width: 5 } -} +}) }).width(300) .height(300) ``` -对比度滤波变换示例 +Example of contrast filtering transformation: ``` ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc: $r('app.media.pngSample') - } + }) }).width(300) .height(300) .contrast(12) ``` -旋转变换示例 +Example of rotation transformation: ``` ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc: $r('app.media.pngSample') - } + }) }).width(300) .height(300) .rotate({angle:90}) .backgroundColor(Color.Pink) ``` -#### 8.监听图片加载成功与失败 +#### 8. Listening for Image Loading Success and Failure ``` -ImageKnifeComponent({ ImageKnifeOption: +ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( { loadSrc: $r("app.media.rabbit"), onLoadListener:{ @@ -242,128 +241,156 @@ ImageKnifeComponent({ ImageKnifeOption: console.info(err) } } - } + }) }).width(100).height(100) ``` -#### 9.ImageKnifeComponent - syncLoad -设置是否同步加载图片,默认是异步加载。建议加载尺寸较小的本地图片时将syncLoad设为true,因为耗时较短,在主线程上执行即可 +#### 9. Use of syncLoad +**syncLoad** sets whether to load the image synchronously. By default, the image is loaded asynchronously. When loading a small image, you are advised to set **syncLoad** to **true** so that the image loading can be quickly completed on the main thread. ``` ImageKnifeComponent({ - imageKnifeOption:{ + imageKnifeOption:new ImageKnifeOption({ loadSrc:$r("app.media.pngSample"), placeholderSrc:$r("app.media.loading") - },syncLoad:true + }),syncLoad:true }) ``` -#### 10.ImageKnifeAnimatorComponent 示例 +#### 10. Use of ImageKnifeAnimatorComponent ``` ImageKnifeAnimatorComponent({ - imageKnifeOption:{ + imageKnifeOption:new ImageKnifeOption({ loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed') - },animatorOption:this.animatorOption + }),animatorOption:this.animatorOption }).width(300).height(300).backgroundColor(Color.Orange).margin({top:30}) ``` -## 接口说明 -### ImageKnife组件 -| 组件名称 | 入参内容 | 功能简介 | -|-----------------------------|---------------------------------|--------| -| ImageKnifeComponent | ImageKnifeOption | 图片显示组件 | -| ImageKnifeAnimatorComponent | ImageKnifeOption、AnimatorOption | 动图控制组件 | +#### 11.图片降采样 示例 +``` +ImageKnifeComponent({ + imageKnifeOption:new ImageKnifeOption({ + loadSrc:$r("app.media.pngSample"), + placeholderSrc:$r('app.media.loading'), + errorholderSrc:$r('app.media.failed'), + downsampleOf: DownsampleStrategy.NONE + }),animatorOption:this.animatorOption + }).width(300).height(300) +``` +#### Reuse Scenario +Clear the component content in the **aboutToRecycle** lifecycle and trigger image loading through watch observeration. +## Available APIs +### ImageKnife +| Component | Parameter | Description | +| --------------------------- | -------------------------------- | ------------ | +| ImageKnifeComponent | ImageKnifeOption | Image display component.| +| ImageKnifeAnimatorComponent | ImageKnifeOption, AnimatorOption| Animated image control component.| -### AnimatorOption参数列表 -| 参数名称 | 入参内容 | 功能简介 | -|-----------------------|-------------------------------------------------------|----------| -| state | AnimationStatus | 播放状态(可选) | -| iterations | number | 播放次数(可选) | -| reverse | boolean | 播放顺序(可选) | +### AnimatorOption +| Parameter | Type | Description | +| ---------- | --------------- | ---------------------------------------- | +| state | AnimationStatus | Playback status. Optional. | +| iterations | number | Number of playback times. Optional. | +| reverse | boolean | Playback order. Optional. | +| onStart | ()=>void | Triggered when the animation starts. Optional. | +| onFinish | ()=>void | Triggered when the animation finishes or stops. Optional.| +| onPause | ()=>void | Triggered when the animation pauses. Optional. | +| onCancel | ()=>void | Triggered when the animation is canceled, that is, when it is reset to its initial state. Optional. | +| onRepeat | ()=>void | Triggered when the animation repeats. Optional. | -### ImageKnifeOption参数列表 +### ImageKnifeOption -| 参数名称 | 入参内容 | 功能简介 | -|-----------------------|-------------------------------------------------------|-----------------| -| loadSrc | string、PixelMap、Resource | 主图展示 | -| placeholderSrc | PixelMap、Resource | 占位图图展示(可选) | -| errorholderSrc | PixelMap、Resource | 错误图展示(可选) | -| objectFit | ImageFit | 主图填充效果(可选) | -| placeholderObjectFit | ImageFit | 占位图填充效果(可选) | -| errorholderObjectFit | ImageFit | 错误图填充效果(可选) | -| writeCacheStrategy | CacheStrategyType | 写入缓存策略(可选) | -| onlyRetrieveFromCache | boolean | 是否跳过网络和本地请求(可选) | -| customGetImage | (context: Context, src: string | 自定义下载图片(可选) | | Resource | 错误占位图数据源 | -| border | BorderOptions | 边框圆角(可选) | -| priority | taskpool.Priority | 加载优先级(可选) | -| context | common.UIAbilityContext | 上下文(可选) | -| progressListener | (progress: number)=>void | 进度(可选) | -| signature | String | 自定义缓存关键字(可选) | -| headerOption | Array | 设置请求头(可选) | -| transformation | PixelMapTransformation | 图片变换(可选) | -| drawingColorFilter | ColorFilter | drawing.ColorFilter | 图片变换(可选) | -| onComplete | (event:EventImage | undefined) => voi | 颜色滤镜效果(可选) | -| onLoadListener | onLoadStart: () => void、onLoadSuccess: (data: string | PixelMap | undefined) => void、onLoadFailed: (err: string) => void| 监听图片加载成功与失败 | +| Parameter | Type | Description | +| --------------------- | ----------------------------------------------------- | ------------------------------ | +| loadSrc | string, PixelMap, Resource | Main image. | +| placeholderSrc | PixelMap, Resource | Placeholder image. Optional. | +| errorholderSrc | PixelMap, Resource | Error image. Optional. | +| objectFit | ImageFit | How the main image is resized to fit its container. Optional. | +| placeholderObjectFit | ImageFit | How the placeholder image is resized to fit its container. Optional. | +| errorholderObjectFit | ImageFit | How the error image is resized to fit its container. Optional. | +| writeCacheStrategy | CacheStrategyType | Cache writing strategy. Optional. | +| onlyRetrieveFromCache | boolean | Whether to skip network and local requests. Optional.| +| customGetImage | (context: Context, src: string | Custom image download. Optional. | +| border | BorderOptions | Border corner. Optional. | +| priority | taskpool.Priority | Load priority. Optional. | +| context | common.UIAbilityContext | Context. Optional. | +| progressListener | (progress: number)=>void | Progress. Optional. | +| signature | String | Custom cache signature. Optional. | +| headerOption | Array\ | Request headers. Optional. | +| transformation | PixelMapTransformation | Image transformation. Optional. | +| drawingColorFilter | ColorFilter | Drawing color filter. Optional. | +| onComplete | (event:EventImage \| undefined)=>void | Callback for image loading completion. Optional. | +| onLoadListener | onLoadStart:()=>void,onLoadSuccess:(data:string\|Pixelmap)=>void | Callback for image loading events. Optional. | +| downsampleOf | DownsampleStrategy | 降采样(可选) | +### 降采样类型 +| 类型 | 相关描述 | +|---------------------|-------------------| +| NONE | 不进行降采样 | +| AT_MOST | 请求尺寸大于实际尺寸不进行放大 | +| FIT_CENTER_MEMORY | 两边自适应内存优先 | +| FIT_CENTER_QUALITY | 两边自适应质量优先 | +| CENTER_OUTSIDE_MEMORY | 宽高缩放比最大的比例,进行缩放适配内存优先 | +| CENTER_OUTSIDE_QUALITY | 宽高缩放比最大的比例,进行缩放适配质量优先 | +| AT_LEAST | 根据宽高的最小的比例,进行适配 | +### ImageKnife -### ImageKnife接口 +| Parameter | Type | Description | +| ----------------- | ------------------------------------------------------------ | -------------------------- | +| initMemoryCache | newMemoryCache: IMemoryCache | Initializes a custom memory cache strategy. | +| initFileCache | context: Context, size: number, memory: number | Initializes the file cache size and quantity | +| preLoadCache | loadSrc: string I ImageKnifeOption | Preloads and returns the file cache path. | +| getCacheImage | loadSrc: string, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string) | Obtains resources from memory or file cache.| +| addHeader | key: string, value: Object | Adds a global HTTP request header. | +| setHeaderOptions | Array | Sets global HTTP request headers. | +| deleteHeader | key: string | Deletes a global HTTP request header. | +| setCustomGetImage | customGetImage?: (context: Context, src: string | PixelMap | +| setEngineKeyImpl | IEngineKey | Sets a global cache key generation strategy. | +| putCacheImage | url: string, pixelMap: PixelMap, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string | Writes to the memory disk cache. | +| removeMemoryCache | url: string | Removes an entry from the memory cache. | +| removeFileCache | url: string | Removes an entry from the file cache. | +### Graphics tRansformation Types (GPUImage Dependency Required) -| 参数名称 | 入参内容 | 功能简介 | -|------------------|-------------------------------------------------------------------------------------------------------|---------------| -| initMemoryCache | newMemoryCache: IMemoryCache | 自定义内存缓存策略 | -| initFileCache | context: Context, size: number, memory: number | 初始化文件缓存数量和大小 | -| preLoadCache | loadSrc: string I ImageKnifeOption | 预加载并返回文件缓存路径 | -| getCacheImage | loadSrc: string, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string) | 从内存或文件缓存中获取资源 | -| addHeader | key: string, value: Object | 全局添加http请求头 | -| setHeaderOptions | Array | 全局设置http请求头 | -| deleteHeader | key: string | 全局删除http请求头 | -| setCustomGetImage | customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise | 全局设置自定义下载 | -| setEngineKeyImpl | IEngineKey | 全局配置缓存key生成策略 | -| putCacheImage | url: string, pixelMap: PixelMap, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string | 写入内存磁盘缓存 | -| removeMemoryCache| url: string | ImageKnifeOption | 清理指定内存缓存 | -| removeFileCache | url: string | ImageKnifeOption | 清理指定磁盘缓存 | -### 图形变换类型(需要为GPUImage添加依赖项) +| Type | Description | +| ------------------------ | ----------------------------- | +| BlurTransformation | Blurs the image. | +| BrightnessTransformation | Applies a brightness filter. | +| CropSquareTransformation | Crops the image to a square. | +| CropTransformation | Crops the image to a custom rectangle. | +| GrayScaleTransformation | Applies a grayscale filter. | +| InvertTransformation | Applies an inversion filter. | +| KuwaharaTransformation | Applies a Kuwahara filter (requires **GPUImage**). | +| MaskTransformation | Applies a mask. | +| PixelationTransformation | Applies a pixelation filter (requires **GPUImage**).| +| SepiaTransformation | Applies a sepia filter (requires **GPUImage**).| +| SketchTransformation | Applies a sketch filter (requires **GPUIImage**). | +| SwirlTransformation | Applies a swirl filter (requires **GPUImage**). | +| ToonTransformation | Applies a cartoon filter (requires **GPUImage**). | +| VignetterTransformation | Applies a vignette filter (requires **GPUImage**). | -| 类型 | 相关描述 | -| ---------------------------------- | ----------------------------- | -| BlurTransformation | 模糊处理 | -| BrightnessTransformation | 亮度滤波器 | -| CropSquareTransformation | 正方形剪裁 | -| CropTransformation | 自定义矩形剪裁 | -| GrayScaleTransformation | 灰度级滤波器 | -| InvertTransformation | 反转滤波器 | -| KuwaharaTransformation | 桑原滤波器(使用GPUIImage) | -| MaskTransformation | 遮罩 | -| PixelationTransformation | 像素化滤波器(使用GPUIImage) | -| SepiaTransformation | 乌墨色滤波器(使用GPUIImage) | -| SketchTransformation | 素描滤波器(使用GPUIImage) | -| SwirlTransformation | 扭曲滤波器(使用GPUIImage) | -| ToonTransformation | 动画滤波器(使用GPUIImage) | -| VignetterTransformation | 装饰滤波器(使用GPUIImage) | - -## 下载安装GPUImage依赖 -方法一:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。 +## Downloading and Installing the GPUImage Dependency +Method 1: In the **Terminal** window, run the following command to install the third-party HAR. DevEco Studio will automatically add the HAR as a dependency to the **oh-package.json5** file of the project. ``` ohpm install @ohos/gpu_transform ``` -方法二: 在工程的oh-package.json5中设置三方包依赖,配置示例如下: +Method 2: Set the third-party HAR as a dependency in the **oh-package.json5** file of the project. The following is a configuration example: ``` "dependencies": { "@ohos/gpu_transform": "^1.0.2" } ``` -## 约束与限制 +## Constraints -在下述版本验证通过: -DevEco Studio 5.0 Canary3(5.0.3.221)--SDK:API12 +This project has been verified in the following version: -## 贡献代码 +DevEco Studio: 5.0 Canary3 (5.0.3.502), SDK: API 12 (5.0.0.31) -使用过程中发现任何问题都可以提 [issue](https://gitee.com/openharmony-tpc/ImageKnife/issues) -给我们,当然,我们也非常欢迎你给我们发 [PR](https://gitee.com/openharmony-tpc/ImageKnife/issues) 。 +## How to Contribute -## 开源协议 +If you find any problem during the use, submit an [Issue](https://gitee.com/openharmony-tpc/ImageKnife/issues) or a [PR](https://gitee.com/openharmony-tpc/ImageKnife/issues) to us. -本项目基于 [Apache License 2.0](https://gitee.com/openharmony-tpc/ImageKnife/blob/master/LICENSE) ,请自由的享受和参与开源。 +## License -## 遗留问题 +This project is licensed under [Apache License 2.0](https://gitee.com/openharmony-tpc/ImageKnife/blob/master/LICENSE). -- ImageKnifeAnimator组件无法设置ImageFit属性 -- ImageKnifeAnimator组件设置border属性无法将图片变为圆角 \ No newline at end of file +## Known Issues + +- The **ImageFit** attribute cannot be set for the **ImageKnifeAnimator** component. +- The **border** attribute of the **ImageKnifeAnimator** component cannot make the image rounded corners. diff --git a/README_zh.md b/README_zh.md new file mode 100644 index 0000000..97522da --- /dev/null +++ b/README_zh.md @@ -0,0 +1,375 @@ +# ImageKnife + +**专门为OpenHarmony打造的一款图像加载缓存库,致力于更高效、更轻便、更简单。** + +## 简介 + +本项目参考开源库 [Glide](https://github.com/bumptech/glide) 进行OpenHarmony的自研版本: + +- 支持自定义内存缓存策略,支持设置内存缓存的大小(默认LRU策略)。 +- 支持磁盘二级缓存,对于下载图片会保存一份至磁盘当中。 +- 支持自定义实现图片获取/网络下载 +- 支持监听网络下载回调进度 +- 继承Image的能力,支持option传入border,设置边框,圆角 +- 继承Image的能力,支持option传入objectFit设置图片缩放,包括objectFit为auto时根据图片自适应高度 +- 支持通过设置transform缩放图片 +- 并发请求数量,支持请求排队队列的优先级 +- 支持生命周期已销毁的图片,不再发起请求 +- 自定义缓存key +- 自定义http网络请求头 +- 支持writeCacheStrategy控制缓存的存入策略(只存入内存或文件缓存) +- 支持preLoadCache预加载图片 +- 支持onlyRetrieveFromCache仅用缓存加载 +- 支持使用一个或多个图片变换,如模糊,高亮等 + +待实现特性 + +- 内存降采样优化,节约内存的占用 +- 支持自定义图片解码 + +注意:3.x版本相对2.x版本做了重大的重构,主要体现在: + +- 使用Image组件代替Canvas组件渲染 +- 重构Dispatch分发逻辑,支持控制并发请求数,支持请求排队队列的优先级 +- 支持通过initMemoryCache自定义策略内存缓存策略和大小 +- 支持option自定义实现图片获取/网络下载 + +因此API及能力上,目前有部分差异,主要体现在: + +- 不支持drawLifeCycle接口,通过canvas自会图片 +- mainScaleType,border等参数,新版本与系统Image保持一致 +- gif/webp动图播放与控制(ImageAnimator实现) +- 抗锯齿相关参数 + +## 下载安装 + +``` +ohpm install @ohos/imageknife + +// 如果需要用文件缓存,需要提前初始化文件缓存 +await ImageKnife.getInstance().initFileCache(context, 256, 256 * 1024 * 1024) +``` + +## 使用说明 + +#### 1.显示本地资源图片 + +``` +ImageKnifeComponent({ + ImageKnifeOption: new ImageKnifeOption({ + loadSrc: $r("app.media.app_icon"), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Auto + }) +}).width(100).height(100) +``` + +#### 2.显示本地context files下文件 + +``` +ImageKnifeComponent({ + ImageKnifeOption: new ImageKnifeOption({ + loadSrc: this.localFile, + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Auto + }) +}).width(100).height(100) +``` + +#### 3.显示网络图片 + +``` +ImageKnifeComponent({ + ImageKnifeOption: new ImageKnifeOption({ + loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Auto + }) +}).width(100).height(100) +``` + +#### 4.自定义下载图片 + +``` +ImageKnifeComponent({ + ImageKnifeOption: new ImageKnifeOption({ + loadSrc: "https://file.atomgit.com/uploads/user/1704857786989_8994.jpeg", + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Auto, + customGetImage: custom + }) +}).width(100).height(100) + +// 自定义实现图片获取方法,如自定义网络下载 +@Concurrent +async function custom(context: Context, src: string | PixelMap | Resource): Promise { + console.info("ImageKnife:: custom download:" + src) + // 举例写死从本地文件读取,也可以自己请求网络图片 + return context.resourceManager.getMediaContentSync($r("app.media.bb").id).buffer as ArrayBuffer +} +``` + +#### 5.监听网络下载进度 + +``` +ImageKnifeComponent({ + ImageKnifeOption: new ImageKnifeOption({ + loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", + progressListener:(progress:number)=>{console.info("ImageKinfe:: call back progress = " + progress)} + }) +}).width(100).height(100) +``` + +#### 6.支持option传入border,设置边框,圆角 + +``` +ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( +{ + loadSrc: $r("app.media.rabbit"), + border: {radius:50} + }) +}).width(100).height(100) +``` + +#### 7.支持option图片变换 + +``` +ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( +{ + loadSrc: $r("app.media.rabbit"), + border: {radius:50}, + transformation: new BlurTransformation(3) + }) +}).width(100).height(100) +``` +多种组合变换用法 + +``` +let transformations: collections.Array = new collections.Array(); +transformations.push(new BlurTransformation(5)); +transformations.push(new BrightnessTransformation(0.2)); +ImageKnifeComponent({ + imageKnifeOption: new ImageKnifeOption({ + loadSrc: $r('app.media.pngSample'), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Contain, + border: { radius: { topLeft: 50, bottomRight: 50 } }, // 圆角设置 + transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined // 图形变换组 +}) +}).width(300) + .height(300) + .rotate({ angle: 90 }) // 旋转90度 + .contrast(12) // 对比度滤波器 +``` +其它变换相关属性,可叠加实现组合变换效果 + +圆形裁剪变换示例 + +``` +ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( + { + loadSrc: $r('app.media.pngSample'), + objectFit: ImageFit.Cover, + border: { radius: 150 } +}) +}).width(300) + .height(300) +``` + +圆形裁剪带边框变换示例 + +``` +ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( + { + loadSrc: $r('app.media.pngSample'), + objectFit: ImageFit.Cover, + border: { radius: 150, color: Color.Red, width: 5 } +}) +}).width(300) + .height(300) +``` + +对比度滤波变换示例 + +``` +ImageKnifeComponent({ + imageKnifeOption: new ImageKnifeOption({ + loadSrc: $r('app.media.pngSample') + }) +}).width(300) + .height(300) + .contrast(12) +``` + +旋转变换示例 + +``` +ImageKnifeComponent({ + imageKnifeOption: new ImageKnifeOption({ + loadSrc: $r('app.media.pngSample') + }) +}).width(300) + .height(300) + .rotate({angle:90}) + .backgroundColor(Color.Pink) +``` + +#### 8.监听图片加载成功与失败 + +``` +ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( +{ + loadSrc: $r("app.media.rabbit"), + onLoadListener:{ + onLoadStart:()=>{ + this.starTime = new Date().getTime() + console.info("Load start: "); + }, + onLoadFailed: (err) => { + console.error("Load Failed Reason: " + err + " cost " + (new Date().getTime() - this.starTime) + " milliseconds"); + }, + onLoadSuccess: (data, imageData) => { + console.info("Load Successful: cost " + (new Date().getTime() - this.starTime) + " milliseconds"); + return data; + }, + onLoadCancel(err){ + console.info(err) + } + } + }) +}).width(100).height(100) +``` +#### 9.ImageKnifeComponent - syncLoad +设置是否同步加载图片,默认是异步加载。建议加载尺寸较小的Resource图片时将syncLoad设为true,因为耗时较短,在主线程上执行即可 +``` +ImageKnifeComponent({ + imageKnifeOption:new ImageKnifeOption({ + loadSrc:$r("app.media.pngSample"), + placeholderSrc:$r("app.media.loading") + }),syncLoad:true + }) +``` +#### 10.ImageKnifeAnimatorComponent 示例 +``` +ImageKnifeAnimatorComponent({ + imageKnifeOption:new ImageKnifeOption({ + loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + placeholderSrc:$r('app.media.loading'), + errorholderSrc:$r('app.media.failed') + }),animatorOption:this.animatorOption + }).width(300).height(300).backgroundColor(Color.Orange).margin({top:30}) +``` +#### 复用场景 +在aboutToRecycle生命周期清空组件内容;通过watch监听触发图片的加载。 +## 接口说明 +### ImageKnife组件 +| 组件名称 | 入参内容 | 功能简介 | +|-----------------------------|---------------------------------|--------| +| ImageKnifeComponent | ImageKnifeOption | 图片显示组件 | +| ImageKnifeAnimatorComponent | ImageKnifeOption、AnimatorOption | 动图控制组件 | + +### AnimatorOption参数列表 +| 参数名称 | 入参内容 | 功能简介 | +|------------|-----------------|----------| +| state | AnimationStatus | 播放状态(可选) | +| iterations | number | 播放次数(可选) | +| reverse | boolean | 播放顺序(可选) | +| onStart | ()=>void | 动画开始播放时触发(可选) | +| onFinish | ()=>void | 动画播放完成时或者停止播放时触发(可选) | +| onPause | ()=>void | 动画暂停播放时触发(可选) | +| onCancel | ()=>void | 动画返回最初状态时触发(可选) | +| onRepeat | ()=>void | 动画重复播放时触发(可选) | + +### ImageKnifeOption参数列表 + +| 参数名称 | 入参内容 | 功能简介 | +|-----------------------|-------------------------------------------------------|-----------------| +| loadSrc | string、PixelMap、Resource | 主图展示 | +| placeholderSrc | PixelMap、Resource | 占位图图展示(可选) | +| errorholderSrc | PixelMap、Resource | 错误图展示(可选) | +| objectFit | ImageFit | 主图填充效果(可选) | +| placeholderObjectFit | ImageFit | 占位图填充效果(可选) | +| errorholderObjectFit | ImageFit | 错误图填充效果(可选) | +| writeCacheStrategy | CacheStrategyType | 写入缓存策略(可选) | +| onlyRetrieveFromCache | boolean | 是否跳过网络和本地请求(可选) | +| customGetImage | (context: Context, src: string | 自定义下载图片(可选) | | Resource | 错误占位图数据源 | +| border | BorderOptions | 边框圆角(可选) | +| priority | taskpool.Priority | 加载优先级(可选) | +| context | common.UIAbilityContext | 上下文(可选) | +| progressListener | (progress: number)=>void | 进度(可选) | +| signature | String | 自定义缓存关键字(可选) | +| headerOption | Array | 设置请求头(可选) | +| transformation | PixelMapTransformation | 图片变换(可选) | +| drawingColorFilter | ColorFilter | drawing.ColorFilter | 图片变换(可选) | +| onComplete | (event:EventImage | undefined) => voi | 颜色滤镜效果(可选) | +| onLoadListener | onLoadStart: () => void、onLoadSuccess: (data: string | PixelMap | undefined) => void、onLoadFailed: (err: string) => void| 监听图片加载成功与失败 | + +### ImageKnife接口 + +| 参数名称 | 入参内容 | 功能简介 | +|------------------|-------------------------------------------------------------------------------------------------------|---------------| +| initMemoryCache | newMemoryCache: IMemoryCache | 自定义内存缓存策略 | +| initFileCache | context: Context, size: number, memory: number | 初始化文件缓存数量和大小 | +| preLoadCache | loadSrc: string I ImageKnifeOption | 预加载并返回文件缓存路径 | +| getCacheImage | loadSrc: string, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string) | 从内存或文件缓存中获取资源 | +| addHeader | key: string, value: Object | 全局添加http请求头 | +| setHeaderOptions | Array | 全局设置http请求头 | +| deleteHeader | key: string | 全局删除http请求头 | +| setCustomGetImage | customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise | 全局设置自定义下载 | +| setEngineKeyImpl | IEngineKey | 全局配置缓存key生成策略 | +| putCacheImage | url: string, pixelMap: PixelMap, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string | 写入内存磁盘缓存 | +| removeMemoryCache| url: string | ImageKnifeOption | 清理指定内存缓存 | +| removeFileCache | url: string | ImageKnifeOption | 清理指定磁盘缓存 | +### 图形变换类型(需要为GPUImage添加依赖项) + +| 类型 | 相关描述 | +| ---------------------------------- | ----------------------------- | +| BlurTransformation | 模糊处理 | +| BrightnessTransformation | 亮度滤波器 | +| CropSquareTransformation | 正方形剪裁 | +| CropTransformation | 自定义矩形剪裁 | +| GrayScaleTransformation | 灰度级滤波器 | +| InvertTransformation | 反转滤波器 | +| KuwaharaTransformation | 桑原滤波器(使用GPUIImage) | +| MaskTransformation | 遮罩 | +| PixelationTransformation | 像素化滤波器(使用GPUIImage) | +| SepiaTransformation | 乌墨色滤波器(使用GPUIImage) | +| SketchTransformation | 素描滤波器(使用GPUIImage) | +| SwirlTransformation | 扭曲滤波器(使用GPUIImage) | +| ToonTransformation | 动画滤波器(使用GPUIImage) | +| VignetterTransformation | 装饰滤波器(使用GPUIImage) | + +## 下载安装GPUImage依赖 +方法一:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。 +``` + ohpm install @ohos/gpu_transform +``` +方法二: 在工程的oh-package.json5中设置三方包依赖,配置示例如下: +``` + "dependencies": { + "@ohos/gpu_transform": "^1.0.2" + } +``` +## 约束与限制 + +在下述版本验证通过: +DevEco Studio 5.0 Canary3(5.0.3.502)--SDK:API12 (5.0.0.31) + +## 贡献代码 + +使用过程中发现任何问题都可以提 [issue](https://gitee.com/openharmony-tpc/ImageKnife/issues) +,当然,也非常欢迎发 [PR](https://gitee.com/openharmony-tpc/ImageKnife/pulls) 共建。 + +## 开源协议 + +本项目基于 [Apache License 2.0](https://gitee.com/openharmony-tpc/ImageKnife/blob/master/LICENSE) ,请自由的享受和参与开源。 + +## 遗留问题 + +- ImageKnifeAnimator组件无法设置ImageFit属性 +- ImageKnifeAnimator组件设置border属性无法将图片变为圆角 \ No newline at end of file diff --git a/build-profile.json5 b/build-profile.json5 index 90f6b5d..9c02fd6 100644 --- a/build-profile.json5 +++ b/build-profile.json5 @@ -7,9 +7,8 @@ { "name": "default", "signingConfig": "default", - "compileSdkVersion": "5.0.0(12)", - "compatibleSdkVersion": "5.0.0(12)", - "runtimeOS": "HarmonyOS", + "compileSdkVersion": 12, + "compatibleSdkVersion": 12 } ], "buildModeSet": [ @@ -38,6 +37,10 @@ "name": "library", "srcPath": "./library" }, + { + "name": "gpu_transform", + "srcPath": "./gpu_transform" + }, { "name": "sharedlibrary", "srcPath": "./sharedlibrary", diff --git a/entry/src/main/ets/common/CustomEngineKeyImpl.ets b/entry/src/main/ets/common/CustomEngineKeyImpl.ets index 06f2901..63437c3 100644 --- a/entry/src/main/ets/common/CustomEngineKeyImpl.ets +++ b/entry/src/main/ets/common/CustomEngineKeyImpl.ets @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { DownsampleStrategy } from '@ohos/imageknife'; import { IEngineKey, ImageKnifeOption, PixelMapTransformation,SparkMD5 ,ImageKnifeRequestSource} from '@ohos/libraryimageknife'; //全局自定义key demo @@ -35,6 +36,9 @@ export class CustomEngineKeyImpl implements IEngineKey { if (imageKnifeOption.transformation) { key += "transformation=" + this.getTransformation(imageKnifeOption.transformation) + ";" } + if ((imageKnifeOption.downsampleOf !== DownsampleStrategy.NONE && imageKnifeOption.downsampleOf !== undefined)) { + key += "downsampleOf" + imageKnifeOption.downsampleOf +"width="+width+"height="+ height + } } return key } diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets index 488dcce..e19ffa7 100644 --- a/entry/src/main/ets/entryability/EntryAbility.ets +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -47,7 +47,6 @@ export default class EntryAbility extends UIAbility { hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); - LogUtil.mLogLevel = LogUtil.ALL // 初始化ImageKnife的文件缓存 await InitImageKnife.init(this.context) ImageKnife.getInstance().setEngineKeyImpl(new CustomEngineKeyImpl()) diff --git a/entry/src/main/ets/pages/DownSamplePage.ets b/entry/src/main/ets/pages/DownSamplePage.ets new file mode 100644 index 0000000..8a59b89 --- /dev/null +++ b/entry/src/main/ets/pages/DownSamplePage.ets @@ -0,0 +1,214 @@ +/* + * 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 { DownsampleStrategy, ImageKnifeOption, } from '@ohos/imageknife'; +import { ImageKnifeComponent } from '@ohos/libraryimageknife'; +import { image } from '@kit.ImageKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { Downsampler } from '@ohos/imageknife/src/main/ets/downsampling/Downsampler'; +import { FileTypeUtil } from '@ohos/imageknife/src/main/ets/utils/FileTypeUtil'; + +@Entry +@ComponentV2 +struct DownSamplePage { + @Local imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ + loadSrc: $r('app.media.startIcon'), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Contain + }) + isBrightness: boolean = false + @Local beforeSampling: number = 0 + @Local afterSampling: number = 0 + @Local SamplingList: SamplingType[] = [ + + new SamplingType(7, "AT_LEAST"), + new SamplingType(1, "AT_MOST"), + + new SamplingType(2, "FIT_CENTER_MEMORY"), + new SamplingType(4, "FIT_CENTER_QUALITY"), + new SamplingType(5, "CENTER_OUTSIDE_MEMORY"), + new SamplingType(6, "CENTER_OUTSIDE_QUALITY"), + new SamplingType(0, "NONE"), + + ] + @Local checked: boolean = false + + updateImageKnifeOption(value: string) { + if (value === 'NONE') { + this.imageKnifeOption = new ImageKnifeOption({ + loadSrc: $r('app.media.pngSample'), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Contain, + downsampleOf: DownsampleStrategy.NONE + }) + this.originalPixMap($r('app.media.pngSample')) + this.afterSamplingFunc($r('app.media.pngSample')) + } else if (value === 'AT_MOST') { + this.imageKnifeOption = new ImageKnifeOption({ + loadSrc: $r('app.media.pngSample'), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Contain, + downsampleOf: DownsampleStrategy.AT_MOST + }) + this.originalPixMap($r('app.media.pngSample')) + this.afterSamplingFunc($r('app.media.pngSample')) + } else if (value === 'FIT_CENTER_MEMORY') { + this.imageKnifeOption = new ImageKnifeOption({ + loadSrc: $r('app.media.pngSample'), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Contain, + downsampleOf: DownsampleStrategy.FIT_CENTER_MEMORY + }) + this.originalPixMap($r('app.media.pngSample')) + this.afterSamplingFunc($r('app.media.pngSample')) + } else if (value === 'FIT_CENTER_QUALITY') { + this.imageKnifeOption = new ImageKnifeOption({ + loadSrc: $r('app.media.pngSample'), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Contain, + downsampleOf: DownsampleStrategy.FIT_CENTER_QUALITY + }) + this.originalPixMap($r('app.media.pngSample')) + this.afterSamplingFunc($r('app.media.pngSample')) + } else if (value === 'CENTER_OUTSIDE_MEMORY') { + this.imageKnifeOption = new ImageKnifeOption({ + loadSrc: $r('app.media.pngSample'), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Contain, + downsampleOf: DownsampleStrategy.CENTER_OUTSIDE_MEMORY + }) + this.originalPixMap($r('app.media.pngSample')) + this.afterSamplingFunc($r('app.media.pngSample')) + } else if (value === 'CENTER_OUTSIDE_QUALITY') { + this.imageKnifeOption = new ImageKnifeOption({ + loadSrc: $r('app.media.pngSample'), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Contain, + downsampleOf: DownsampleStrategy.CENTER_OUTSIDE_QUALITY + }) + this.originalPixMap($r('app.media.pngSample')) + this.afterSamplingFunc($r('app.media.pngSample')) + } else { + this.imageKnifeOption = new ImageKnifeOption({ + loadSrc: $r('app.media.pngSample'), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Contain, + downsampleOf: DownsampleStrategy.AT_LEAST + }) + this.originalPixMap($r('app.media.pngSample')) + this.afterSamplingFunc($r('app.media.pngSample')) + } + } + + async afterSamplingFunc(imgs: Resource) { + let img: Uint8Array = await getContext(this).resourceManager.getMediaContent(imgs); + let imageSource: image.ImageSource = image.createImageSource(img.buffer.slice(0)); + let fileTypeUtil = new FileTypeUtil(); + let typeValue = fileTypeUtil.getFileType(img.buffer.slice(0)) as string; + let decodingOptions: image.DecodingOptions = { + editable: true, + desiredPixelFormat: 3, + } + let imageInfo = await imageSource.getImageInfo() + + if (this.imageKnifeOption.downsampleOf !== DownsampleStrategy.NONE){ + let reqSize = + new Downsampler().calculateScaling(typeValue, imageInfo.size.width, imageInfo.size.height, 300, + 300, this.imageKnifeOption.downsampleOf) + decodingOptions = { + editable: true, + desiredSize: { + width: reqSize.width, + height: reqSize.height + } + } + } + + // 创建pixelMap + imageSource.createPixelMap(decodingOptions).then((pixelMap: image.PixelMap) => { + this.afterSampling = pixelMap.getPixelBytesNumber() + }).catch((err: BusinessError) => { + console.error("Failed to create PixelMap") + }); + } + + async originalPixMap(imgs: Resource,) { + let img: Uint8Array = await getContext(this).resourceManager.getMediaContent(imgs); + let imageSource: image.ImageSource = image.createImageSource(img.buffer.slice(0)); + let decodingOptions: image.DecodingOptions = { + editable: true, + desiredPixelFormat: 3, + } + // 创建pixelMap + imageSource.createPixelMap(decodingOptions).then((pixelMap: image.PixelMap) => { + this.beforeSampling = pixelMap.getPixelBytesNumber() + }).catch((err: BusinessError) => { + console.error("Failed to create PixelMap") + }); + } + getResourceString(res:Resource){ + return getContext().resourceManager.getStringSync(res.id) + } + build() { + Scroll() { + Column() { + ForEach(this.SamplingList, (item: SamplingType, index) => { + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Radio({ value: item.value + 'radio', group: 'radioGroup' }) + .height(50) + .width(50) + .checked(this.checked) + .onClick(() => { + this.updateImageKnifeOption(item.value) + }) + Text(this.getResourceString($r('app.string.Sampling_pecification'))+ item.value).fontSize(20) + } + }, (item: SamplingType) => JSON.stringify(item)) + Column() { + Text(`${this.getResourceString($r('app.string.Unreal_samples'))}:${this.beforeSampling}`).fontSize(20) + Text(`${ this.getResourceString($r('app.string.After_the_sampling'))}:${this.afterSampling}`).fontSize(20) + } + + ImageKnifeComponent({ + imageKnifeOption: this.imageKnifeOption + }) + .height(300) + .width(300) + .borderWidth(1) + .borderColor(Color.Pink) + } + + } + .height('100%') + .width('100%') + } +} + +class SamplingType { + key: number + value: string + + constructor(key: number, value: string) { + this.key = key + this.value = value + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/ImageAnimatorPage.ets b/entry/src/main/ets/pages/ImageAnimatorPage.ets index bde0508..5837f7f 100644 --- a/entry/src/main/ets/pages/ImageAnimatorPage.ets +++ b/entry/src/main/ets/pages/ImageAnimatorPage.ets @@ -1,41 +1,93 @@ -import { AnimatorOption, ImageKnifeAnimatorComponent } from "@ohos/libraryimageknife" +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { AnimatorOption, ImageKnifeAnimatorComponent,ImageKnifeOption } from "@ohos/libraryimageknife" @Entry -@Component +@ComponentV2 struct ImageAnimatorPage { - @State animatorOption: AnimatorOption = { + @Local animatorOption: AnimatorOption = new AnimatorOption({ state: AnimationStatus.Running, - iterations: -1 - } + iterations: -1, + onFinish:()=>{ + console.log("ImageKnifeAnimatorComponent animatorOption onFinish") + }, + onStart:()=>{ + console.log("ImageKnifeAnimatorComponent animatorOption onStart") + }, + onPause:()=>{ + console.log("ImageKnifeAnimatorComponent animatorOption onPause") + }, + onCancel:()=>{ + console.log("ImageKnifeAnimatorComponent animatorOption onCancel") + }, + onRepeat:()=>{ + console.log("ImageKnifeAnimatorComponent animatorOption onRepeat") + } + }) + @Local animatorOption1: AnimatorOption = new AnimatorOption({ + state: AnimationStatus.Initial + }) + @Local animatorOption2: AnimatorOption = new AnimatorOption({ + state: AnimationStatus.Initial, + reverse: true + }) build() { Column(){ Flex(){ - Button("播放").onClick(()=>{ + Button($r('app.string.Play')).onClick(()=>{ this.animatorOption.state = AnimationStatus.Running }) - Button("暂停").onClick(()=>{ + Button($r('app.string.Pause')).onClick(()=>{ this.animatorOption.state = AnimationStatus.Paused }) - Button("停止").onClick(()=>{ + Button($r('app.string.Stop')).onClick(()=>{ this.animatorOption.state = AnimationStatus.Stopped }) - Button("无限循环").onClick(()=>{ + Button($r('app.string.Infinite_loop')).onClick(()=>{ this.animatorOption.iterations = -1 }) - Button("播放一次").onClick(()=>{ + Button($r('app.string.Play_once')).onClick(()=>{ this.animatorOption.iterations = 1 }) - Button("播放两次").onClick(()=>{ + Button($r('app.string.Play_twice')).onClick(()=>{ this.animatorOption.iterations = 2 }) } ImageKnifeAnimatorComponent({ - imageKnifeOption:{ + imageKnifeOption:new ImageKnifeOption({ loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed') - },animatorOption:this.animatorOption - }).width(300).height(300).backgroundColor(Color.Orange).margin({top:30}) + }),animatorOption:this.animatorOption + }).width(200).height(200).backgroundColor(Color.Orange).margin({top:30}) + Text($r('app.string.Display_the_first_frame')).fontSize(20) + ImageKnifeAnimatorComponent({ + imageKnifeOption:new ImageKnifeOption({ + loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + placeholderSrc:$r('app.media.loading'), + errorholderSrc:$r('app.media.failed') + }),animatorOption:this.animatorOption1 + }).width(200).height(200).backgroundColor(Color.Orange).margin({top:30}) + Text($r('app.string.Display_the_last_frame')).fontSize(20) + ImageKnifeAnimatorComponent({ + imageKnifeOption:new ImageKnifeOption({ + loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + placeholderSrc:$r('app.media.loading'), + errorholderSrc:$r('app.media.failed') + }),animatorOption:this.animatorOption2 + }).width(200).height(200).backgroundColor(Color.Orange).margin({top:30}) }.width("100%").height("100%") } } \ No newline at end of file diff --git a/entry/src/main/ets/pages/ImageTransformation.ets b/entry/src/main/ets/pages/ImageTransformation.ets index 844ed62..5a88f34 100644 --- a/entry/src/main/ets/pages/ImageTransformation.ets +++ b/entry/src/main/ets/pages/ImageTransformation.ets @@ -35,17 +35,17 @@ import { import { collections } from '@kit.ArkTS' @Entry -@Component +@ComponentV2 struct ImageTransformation { - @State imageKnifeOption: ImageKnifeOption = { + @Local imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r('app.media.pngSample'), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Contain - } - @State isRound: boolean = false; - @State isContrast: boolean = false; - @State isRotate: boolean = false; + }) + @Local isRound: boolean = false; + @Local isContrast: boolean = false; + @Local isRotate: boolean = false; isBlur: boolean = false isBrightness: boolean = false isGrayScale: boolean = false; @@ -78,7 +78,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('模糊效果').fontSize(20) + Text($r('app.string.Blur_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -91,7 +91,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('高亮效果').fontSize(20) + Text($r('app.string.Highlighting_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -104,7 +104,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('灰化效果').fontSize(20) + Text($r('app.string.Ashing_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -117,7 +117,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('反转效果').fontSize(20) + Text($r('app.string.Inverse_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -130,7 +130,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('动画滤镜效果').fontSize(20) + Text($r('app.string.Animation_filter_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -143,7 +143,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('裁剪圆形效果').fontSize(20) + Text($r('app.string.Crop_circular_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -156,7 +156,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('裁剪圆形带边框效果').fontSize(20) + Text($r('app.string.Crop_circular_with_border_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -168,7 +168,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('对比度效果').fontSize(20) + Text($r('app.string.Contrast_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -181,7 +181,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('乌墨色滤波效果').fontSize(20) + Text($r('app.string.Black_ink_filtering_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -193,7 +193,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('旋转效果').fontSize(20) + Text($r('app.string.Rotate')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -206,7 +206,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('圆角效果').fontSize(20) + Text($r('app.string.Corners')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -219,7 +219,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('桑原滤波效果').fontSize(20) + Text($r('app.string.Kuwahara_Filter_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -232,7 +232,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('像素化滤波效果').fontSize(20) + Text($r('app.string.Pixelated_Filter_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -245,7 +245,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('素描滤波效果').fontSize(20) + Text($r('app.string.Sketch_Filter_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -258,7 +258,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('扭曲滤波效果').fontSize(20) + Text($r('app.string.Distortion_Filter_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -271,7 +271,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('装饰滤波效果').fontSize(20) + Text($r('app.string.Decorative_Filter_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -284,7 +284,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('正方形裁剪效果').fontSize(20) + Text($r('app.string.Square_cutting_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -297,7 +297,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('上方裁剪效果').fontSize(20) + Text($r('app.string.Top_cutting_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -310,7 +310,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('中间裁剪效果').fontSize(20) + Text($r('app.string.Middle_cutting_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -323,7 +323,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('底下裁剪效果').fontSize(20) + Text($r('app.string.Bottom_cutting_effect')).fontSize(20) } Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { @@ -336,7 +336,7 @@ struct ImageTransformation { }) .width(30) .height(30) - Text('遮罩效果').fontSize(20) + Text($r('app.string.Mask_effect')).fontSize(20) } if (this.isContrast) { @@ -412,14 +412,14 @@ struct ImageTransformation { if (this.isMask) { transformations.push(new MaskTransformation($r('app.media.mask_starfish'))); } - this.imageKnifeOption = { + this.imageKnifeOption = new ImageKnifeOption({ loadSrc: $r('app.media.pngSample'), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Contain, border: { radius: this.isRound ? { topLeft: 50, bottomRight: 50 } : 0 }, transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined - } + }) if (this.isCropCircle) { this.imageKnifeOption.objectFit = ImageFit.Cover; this.imageKnifeOption.border = { radius: 150 }; diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index 3383f6a..def6a65 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -15,147 +15,172 @@ import router from '@system.router'; @Entry -@Component +@ComponentV2 struct Index { - + getResourceString(res: Resource) { + return getContext().resourceManager.getStringSync(res.id) + } aboutToAppear(): void { } build() { - Scroll(){ + Scroll() { Column() { - Button("测试ImageAnimator组件").onClick(()=>{ + Button($r('app.string.Test_ImageAnimator')).onClick(() => { router.push({ uri: 'pages/ImageAnimatorPage', }); }) - Button("测试加载多张相同图片").margin({top:10}).onClick(()=>{ + Button($r('app.string.Test_multiple_images')).margin({ top: 10 }).onClick(() => { router.push({ uri: 'pages/TestCommonImage', }); }) - Button("测试HSP场景预加载").margin({top:10}).onClick(()=>{ - router.push({ - uri: 'pages/TestHspPreLoadImage', + Button(this.getResourceString($r('app.string.Multiple_images')) + " + reuse + LazyForeach") + .margin({ top: 10 }) + .onClick(() => { + router.push({ + uri: 'pages/UserPage', - }); - }) - Button("单个图片使用").margin({top:10}).onClick(()=>{ + }); + }) + Button($r('app.string.Test_SingleImage')).margin({ top: 10 }).onClick(() => { router.push({ uri: 'pages/SingleImage', }); }) - Button("全局自定义下载").margin({top:10}).onClick(()=>{ + Button($r('app.string.Image_Downsampling_Functionality')).margin({top:10}).onClick(()=>{ router.push({ - uri: 'pages/TestSetCustomImagePage', - + uri: 'pages/DownSamplePage', }); }) - Button("多图 + LazyForEach").margin({top:10}).onClick(()=>{ - router.push({ - uri: 'pages/ManyPhotoShowPage', - - }); - }) - Button("多图 + reuse + LazyForeach").margin({top:10}).onClick(()=>{ - router.push({ - uri: 'pages/UserPage', - - }); - }) - Button("长图显示").margin({top:10}).onClick(()=>{ + Button($r('app.string.Display_long_image')).margin({ top: 10 }).onClick(() => { router.push({ uri: 'pages/LongImagePage', }); }) - Button("缩放图片").margin({top:10}).onClick(()=>{ - router.push({ - uri: 'pages/TransformPage', - - }); - }) - Button("消息+List").margin({top:10}).onClick(()=>{ - router.push({ - uri: 'pages/TestImageFlash', - - }); - }) - Button("自定义缓存key").margin({top:10}).onClick(()=>{ - router.push({ - uri: 'pages/SignatureTestPage', - - }); - }) - Button("预加载图片到文件缓存").margin({top:10}).onClick(()=>{ - router.push({ - uri: 'pages/TestPrefetchToFileCache', - - }); - }) - Button("从缓存获取图片显示").margin({top:10}).onClick(()=>{ - router.push({ - uri: 'pages/TestIsUrlExist', - - }); - }) - Button("测试单个请求头").margin({top:10}).onClick(()=>{ - router.push({ - uri: 'pages/TestHeader', - - }); - }) - Button("测试写入缓存策略").margin({top:10}).onClick(()=>{ - router.push({ - uri: 'pages/TestWriteCacheStage', - - }); - }) - Button("图片变换").margin({top:10}).onClick(()=>{ + Button($r('app.string.Image_Transformation')).margin({ top: 10 }).onClick(() => { router.push({ uri: 'pages/ImageTransformation', }); }) + Button($r('app.string.Test_media_URL')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/dataShareUriLoadPage', - Button("不同的ObjectFit").margin({top:10}).onClick(()=>{ + }); + }) + Button($r('app.string.Different_ObjectFit')).margin({ top: 10 }).onClick(() => { router.push({ uri: 'pages/ObjectFitPage', }); }) + Button($r('app.string.Custom_cache_key')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/SignatureTestPage', - Button('测试图片加载成功/失败事件').margin({top:10}).onClick(()=>{ + }); + }) + Button($r('app.string.Test_image_loading_success_or_failure_events')).margin({ top: 10 }).onClick(() => { router.push({ uri: 'pages/LoadStatePage', }) }) + Button($r('app.string.Image_scaling')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TransformPage', - Button('测试移除图片缓存接口').margin({top:10}).onClick(()=>{ + }); + }) + + Button($r('app.string.Test_HSP')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TestHspPreLoadImage', + + }); + }) + + Button($r('app.string.Test_custom_download')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TestSetCustomImagePage', + + }); + }) + + Button(this.getResourceString($r('app.string.Message_list')) + " + List").margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TestImageFlash', + + }); + }) + + Button($r('app.string.Preloading_images_to_cache')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TestPrefetchToFileCache', + + }); + }) + Button($r('app.string.Retrieve_image_display_from_cache')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TestIsUrlExist', + + }); + }) + Button($r('app.string.Test_single_request_header')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TestHeader', + + }); + }) + Button($r('app.string.Test_write_cache_strategy')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TestWriteCacheStage', + + }); + }) + + + Button($r('app.string.Test_removing_image_cache_interface')).margin({ top: 10 }).onClick(() => { router.push({ uri: 'pages/TestRemoveCache', }); }) - Button("测试错误图显示").margin({top:10}).onClick(()=>{ + Button($r('app.string.Test_error_image_display')).margin({ top: 10 }).onClick(() => { router.push({ uri: 'pages/TestErrorHolderPage', }); }) - Button("测试媒体url").margin({top:10}).onClick(()=>{ + Button($r('app.string.Test_Task_error')).margin({ top: 10 }).onClick(() => { router.push({ - uri: 'pages/dataShareUriLoadPage', - + uri: 'pages/TestTaskResourcePage', + }); + }) + Button($r('app.string.test_cache_btn')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TestCacheDataPage', + }); + }) + Button($r('app.string.test_change_color_btn')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TestChangeColorPage', + }); + }) + Button($r('app.string.test_cancel_callback_btn')).margin({ top: 10 }).onClick(() => { + router.push({ + uri: 'pages/TestLoadCancelListenerPage', }); }) - } - } .width('100%') + }.width('100%') .height('100%') } } \ No newline at end of file diff --git a/entry/src/main/ets/pages/ListPage.ets b/entry/src/main/ets/pages/ListPage.ets index c604d97..1c6d614 100644 --- a/entry/src/main/ets/pages/ListPage.ets +++ b/entry/src/main/ets/pages/ListPage.ets @@ -15,11 +15,11 @@ import { ImageKnifeComponent, ImageKnifeOption } from '@ohos/libraryimageknife'; @Entry -@Component +@ComponentV2 struct ListPage { private data: string[] = [] - @State ImageKnifeOption: ImageKnifeOption = { loadSrc: $r('app.media.startIcon')} + @Local ImageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r('app.media.startIcon')}) aboutToAppear(): void { diff --git a/entry/src/main/ets/pages/LoadStatePage.ets b/entry/src/main/ets/pages/LoadStatePage.ets index 80c41a3..876a74e 100644 --- a/entry/src/main/ets/pages/LoadStatePage.ets +++ b/entry/src/main/ets/pages/LoadStatePage.ets @@ -16,12 +16,12 @@ import { ImageKnifeComponent, ImageKnifeOption } from "@ohos/libraryimageknife" import matrix4 from '@ohos.matrix4' @Entry -@Component +@ComponentV2 struct LoadStatePage { starTime:number = new Date().getTime() - @State ImageKnifeOption: ImageKnifeOption = { + @Local ImageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r("app.media.rabbit"), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), @@ -35,22 +35,22 @@ struct LoadStatePage { }, }, border: { radius: 50 } - } - @State imageKnifeOption1: ImageKnifeOption = { + }) + @Local imageKnifeOption1: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r('app.media.startIcon') - } - @State message: string = "" - @State currentWidth: number = 200 - @State currentHeight: number = 200 - @State typeValue: string = "" + }) + @Local message: string = "" + @Local currentWidth: number = 200 + @Local currentHeight: number = 200 + @Local typeValue: string = "" build() { Column() { - Text('测试失败场景请先关闭网络,并保证本地没有此网络图片的缓存') + Text($r('app.string.TIPS')) .margin({ top: 20 }) Row() { - Button('测试失败/成功场景') + Button($r('app.string.Test_failure_success')) .onClick(() => { - this.ImageKnifeOption = { + this.ImageKnifeOption = new ImageKnifeOption({ loadSrc: "https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), @@ -75,15 +75,17 @@ struct LoadStatePage { onComplete:(event)=>{ console.error("Load onComplete width:"+event?.width , " height:"+event?.height , " componentWidth:"+event?.componentWidth," componentHeight:" + event?.componentHeight); } - } + }) }) } .margin({ top: 20 }) - Text(this.typeValue) + Text($r('app.string.image_format',this.typeValue)) + Text($r('app.string.image_width',this.currentWidth)) + Text($r('app.string.image_height',this.currentHeight)) ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption }).height(this.currentHeight).width(this.currentWidth) .margin({ top: 20 }) - Button("自定义下载失败").onClick(()=>{ - this.imageKnifeOption1 = { + Button($r('app.string.Custom_download_failed')).onClick(()=>{ + this.imageKnifeOption1 = new ImageKnifeOption({ loadSrc: "abc", placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed'), @@ -93,7 +95,7 @@ struct LoadStatePage { this.message = "err:" + err } } - } + }) }).margin({ top: 20 }) Text(this.message).fontSize(20).margin({ top: 20 }) ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).height(this.currentHeight).width(this.currentWidth) diff --git a/entry/src/main/ets/pages/LongImagePage.ets b/entry/src/main/ets/pages/LongImagePage.ets index c8f6e1f..0d09a96 100644 --- a/entry/src/main/ets/pages/LongImagePage.ets +++ b/entry/src/main/ets/pages/LongImagePage.ets @@ -12,10 +12,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeComponent } from '@ohos/libraryimageknife' +import { ImageKnifeComponent,ImageKnifeOption } from '@ohos/libraryimageknife' @Entry -@Component +@ComponentV2 struct LongImagePage { build() { @@ -25,13 +25,13 @@ struct LongImagePage { // Image($r("app.media.aaa")).objectFit(ImageFit.Auto).width(200) ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc:"https://wx2.sinaimg.cn/mw690/006HyQKGgy1hnqp08dw09j30u04twu0x.jpg", //src:$r("app.media.aaa"), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.failed"), objectFit: ImageFit.Auto - } + }) }) } .height('100%') .width('100%') diff --git a/entry/src/main/ets/pages/ManyPhotoShowPage.ets b/entry/src/main/ets/pages/ManyPhotoShowPage.ets deleted file mode 100644 index 0719141..0000000 --- a/entry/src/main/ets/pages/ManyPhotoShowPage.ets +++ /dev/null @@ -1,2915 +0,0 @@ -/* - * Copyright (C) 2024 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { ImageKnifeComponent } from '@ohos/libraryimageknife'; - - - -@Entry -@Component -struct ManyPhotoShowPage { - private data: TestDataSource = new TestDataSource(); - - private elementScroller: Scroller = new Scroller() - - build() { - Column() { - - List({ space: 20, scroller: this.elementScroller }) { - LazyForEach(this.data, (item: Material, index) => { - ListItem() { - Column() { - Stack({ alignContent: Alignment.BottomEnd }) { - // 滤镜图片 - // ImageKnifeComponent({ imageKnifeOption: { - // loadSrc: item.thumbnail, - // mainScaleType: ScaleType.FIT_XY, - // } }) - ImageKnifeComponent({imageKnifeOption:{ - loadSrc: item.thumbnail, - //src:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", - // src: this.localFile, - placeholderSrc:$r("app.media.loading"), - errorholderSrc:$r("app.media.failed"), - objectFit: ImageFit.Auto, - border: {radius:50} - }}) - } - .width(56).height(56) - //滤镜标题 - Text(item.name) - .fontSize(10) - .maxLines(1) - .fontColor(Color.White) - .textAlign(TextAlign.Center) - .layoutWeight(1) - .width('100%') - .backgroundColor(Color.Orange) - } - .width(56) - .height(72) - .clip(true) - .borderRadius(4) - } - }, (item: Material) => item.material_id) - } - .listDirection(Axis.Horizontal) - .width('100%') - .height(72) - - } - } - - - -} - - -class BasicDataSource implements IDataSource { - private listeners: DataChangeListener[] = []; - - public totalCount(): number { - return 0; - } - - public getData(index: number):T | undefined { - return undefined; - } - - 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 Material{ - material_id: string =''; - thumbnail: string = ''; - name: string =''; -} - -class TestDataSource extends BasicDataSource { - private dataArray: Material[] = [ - { - name: "测试CBC", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/R9aT7lpEEVxawo4.jpeg!thumb-w321-webp75", - material_id: "5060118818" - }, - { - name: "通用天空1像素测试", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/1V6c63lKLGPlKVo.png!thumb-w321-webp75", - material_id: "506009997" - }, - { - name: "像素测试+bin", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/2NxCo49xAB1L96K.jpeg!thumb-w321-webp75", - material_id: "506009998" - }, - { - name: "像素测试", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/P6gH3pWBGw7L6dD.jpeg!thumb-w321-webp75", - material_id: "506009999" - }, - { - name: "名称一", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/MKosgAWo5VKpo9Z.jpeg!thumb-w321-webp75", - material_id: "50600999" - }, - { - name: "质感", - thumbnail: "https://xximg1.meitudata.com/XepUPNP4WP.png!thumb-w321-webp75", - material_id: "20076061608" - }, - { - name: "抠图批量拼图", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/pA2S9lvNpMY4X3N.jpeg!thumb-w321-webp75", - material_id: "506002368" - }, - { - name: "修改测试", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/69MiA9VxKdkzjWR.png!thumb-w321-webp75", - material_id: "50600229" - }, - { - name: "兰桂坊-艺术", - thumbnail: "https://material-center-pre.meitudata.com/material/image/6228901fd8ddd6350.jpeg!thumb-w321-webp75", - material_id: "50639415" - }, - { - name: "有门槛的我", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/P98i3gdgDw3Rmx5.png!thumb-w321-webp75", - material_id: "506093422" - }, - { - name: "222嗯嗯1", - thumbnail: "https://stage.meitudata.com/public/creator/ed70a3c696c8e7b.jpg!thumb-w321-webp75", - material_id: "50693244" - }, - { - name: "滤镜流程11", - thumbnail: "https://material-center-pre.meitudata.com/material/image/6194a57b5b9ef7678.jpg!thumb-w321-webp75", - material_id: "506017292" - }, - { - name: "hsxTest11.8", - thumbnail: "https://material-center-pre.meitudata.com/material/image/6189f29b48f165954.jpg!thumb-w321-webp75", - material_id: "506530963" - }, - { - name: "旧-美食手绘1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/0lZCW9BrDgMroLL.jpeg!thumb-w321-webp75", - material_id: "5060002001" - }, - { - name: "Forest", - thumbnail: "https://xximg1.meitudata.com/V2jTkoYa96.jpeg!thumb-w321-webp75", - material_id: "20106191835" - }, - { - name: "春樱", - thumbnail: "https://xximg1.meitudata.com/k5DIOxgJpN.jpeg!thumb-w321-webp75", - material_id: "20076291634" - }, - { - name: "GT G", - thumbnail: "http://xximg2.meitudata.com/Z9DCJpkB2l.jpeg!thumb-w321-webp75", - material_id: "20085081215" - }, - { - name: "测", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/zwdte8R2e5KMMmm.jpeg!thumb-w321-webp75", - material_id: "506000000" - }, - { - name: "五一鱼块", - thumbnail: "https://xximg1.meitudata.com/mlahyxNxjN.jpeg!thumb-w321-webp75", - material_id: "20076292056" - }, - { - name: "青空", - thumbnail: "https://xximg1.meitudata.com/8OkUwBympV.jpeg!thumb-w321-webp75", - material_id: "20106311844" - }, - { - name: "质感", - thumbnail: "https://xximg1.meitudata.com/9nzUydyWeB.png!thumb-w321-webp75", - material_id: "20076061589" - }, - { - name: "测试CBC", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/R9aT7lpEEVxawo4.jpeg!thumb-w321-webp75", - material_id: "5060118818" - }, - { - name: "像素天空抠图测试", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/Ymju0r3deAl7xoJ.jpeg!thumb-w321-webp75", - material_id: "506005680" - }, - { - name: "像素滤镜测试", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/8zPcB0y4vdreG1d.jpeg!thumb-w321-webp75", - material_id: "506019998" - }, - { - name: "通用天空1像素测试", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/1V6c63lKLGPlKVo.png!thumb-w321-webp75", - material_id: "506009997" - }, - { - name: "人像风格化", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/VYwIWp0E52v5vaE.jpeg!thumb-w321-webp75", - material_id: "50600288888" - }, - { - name: "像素+边框测试", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/Z9dtor442OY76KV.jpeg!thumb-w321-webp75", - material_id: "506005678" - }, - { - name: "像素测试+bin", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/2NxCo49xAB1L96K.jpeg!thumb-w321-webp75", - material_id: "506009998" - }, - { - name: "像素测试", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/P6gH3pWBGw7L6dD.jpeg!thumb-w321-webp75", - material_id: "506009999" - }, - { - name: "兰桂坊-艺术", - thumbnail: "https://material-center-pre.meitudata.com/material/image/6228901fd8ddd6350.jpeg!thumb-w321-webp75", - material_id: "50639415" - }, - { - name: "藤黄", - thumbnail: "https://xximg1.meitudata.com/KnJSa8X496.jpeg!thumb-w321-webp75", - material_id: "20076281728" - }, - { - name: "梅幸茶", - thumbnail: "https://xximg1.meitudata.com/GYGcPokEQ3.jpeg!thumb-w321-webp75", - material_id: "20076281771" - }, - { - name: "五一鱼块", - thumbnail: "https://xximg1.meitudata.com/mlahyxNxjN.jpeg!thumb-w321-webp75", - material_id: "20076292056" - }, - { - name: "12.妆容滤镜层级控制", - thumbnail: "https://xxtool-release.zone1.meitudata.com/deRP83dkdlR4.png!thumb-w321-webp75", - material_id: "200720002017" - }, - { - name: "12.妆容滤镜层级控制1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/rPJ1vKjJY6jz.png!thumb-w321-webp75", - material_id: "200720002018" - }, - { - name: "13.3D打光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/z8K3P249ZmVW.png!thumb-w321-webp75", - material_id: "200720002020" - }, - { - name: "14.新bling效果", - thumbnail: "https://xxtool-release.zone1.meitudata.com/e4xlVkl0BzMy.png!thumb-w321-webp75", - material_id: "200720003001" - }, - { - name: "美食手绘效果2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/B6MleZrWeMvj.jpg!thumb-w321-webp75", - material_id: "200720002002" - }, - { - name: "16.美食手绘效果", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xEke61aRK6dg.jpg!thumb-w321-webp75", - material_id: "200720002003" - }, - { - name: "17.素材无人脸规则", - thumbnail: "https://xxtool-release.zone1.meitudata.com/B6MleZ1ae5Ry.jpg!thumb-w321-webp75", - material_id: "200720002010" - }, - { - name: "18.径向色散效果", - thumbnail: "https://xxtool-release.zone1.meitudata.com/EyvzkoPgakDL.jpg!thumb-w321-webp75", - material_id: "200720002012" - }, - { - name: "胶片滤镜2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/3zVRo1E16vZG.png!thumb-w321-webp75", - material_id: "200720002008" - }, - { - name: "20.胶片滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/3zVRo1EdvZ0W.jpg!thumb-w321-webp75", - material_id: "200720002009" - }, - { - name: "21.虚化滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/1mLMv1kd2BLZ.jpg!thumb-w321-webp75", - material_id: "200720002011" - }, - { - name: "22.天空分割滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/8NXRMzE019aB.jpg!thumb-w321-webp75", - material_id: "200720002006" - }, - { - name: "23.妆容滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/0XDRo7xdPkVx.jpg!thumb-w321-webp75", - material_id: "200720002007" - }, - { - name: "1.随机滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/V06dg1RJ8pDr.jpg!thumb-w321-webp75", - material_id: "200720001004" - }, - { - name: "15.实时取色效果", - thumbnail: "https://xxtool-release.zone1.meitudata.com/2p5Ro1VDZvxR.png!thumb-w321-webp75", - material_id: "200720001001" - }, - { - name: "10.不带妆容", - thumbnail: "https://xxtool-release.zone1.meitudata.com/8NXRMz5oypN7.jpeg!thumb-w321-webp75", - material_id: "200720002030" - }, - { - name: "16.美食手绘效果1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/1mLMWyxNVMAe.jpg!thumb-w321-webp75", - material_id: "506088836" - }, - { - name: "素材多比例滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/7d8RoA7DXYB9.jpg!thumb-w321-webp75", - material_id: "200720002013" - }, - { - name: "安德森粉", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/OXOHRLBqVppoxEY.png!thumb-w321-webp75", - material_id: "20079982224" - }, - { - name: "安德森黄", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/bBZcY8dnYDqZE5v.png!thumb-w321-webp75", - material_id: "20079982225" - }, - { - name: "安德森冷", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/x5NF1JLYvqGENkj.png!thumb-w321-webp75", - material_id: "20079982226" - }, - { - name: "安德森暖青", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/KeVUpByXzRp0oPK.png!thumb-w321-webp75", - material_id: "20079982227" - }, - { - name: "高光+100", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/PB2cdnDGN0nqb8q.png!thumb-w321-webp75", - material_id: "20079981113" - }, - { - name: "高光-100", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/XepU5b6OZKW90Yv.png!thumb-w321-webp75", - material_id: "20079981114" - }, - { - name: "阴影+100", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/KeVUp1oQD6W8xg5.png!thumb-w321-webp75", - material_id: "20079981115" - }, - { - name: "阴影-100", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/GPWcJeyweKZR69d.png!thumb-w321-webp75", - material_id: "20079981116" - }, - { - name: "cg无素材", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/LEasj5wg0o10JzO.png!thumb-w321-webp75", - material_id: "20079981112" - }, - { - name: "会员电影胶片", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8QzFJLZVqLlvj8v.png!thumb-w321-webp75", - material_id: "20079982254" - }, - { - name: "秋1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/KeVUpByPLRGkgYj.jpeg!thumb-w321-webp75", - material_id: "20079982230" - }, - { - name: "秋2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/6kQUqeG39YJlEo1.jpeg!thumb-w321-webp75", - material_id: "20079982231" - }, - { - name: "秋3", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/KeVUpByPvxBLldg.jpeg!thumb-w321-webp75", - material_id: "20079982232" - }, - { - name: "秋4", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/3YDh1Dl6xY30nqD.jpeg!thumb-w321-webp75", - material_id: "20079982233" - }, - { - name: "秋5", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/9nzUG9DeeGmn94G.jpeg!thumb-w321-webp75", - material_id: "20079982234" - }, - { - name: "秋6", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/YkpUjYwlPanLVNg.jpeg!thumb-w321-webp75", - material_id: "20079982235" - }, - { - name: "秋7", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/OXOHRLB3YBPbD0o.jpeg!thumb-w321-webp75", - material_id: "20079982236" - }, - { - name: "秋8", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/dWZS0X3P2LjqJ5x.jpeg!thumb-w321-webp75", - material_id: "20079982240" - }, - { - name: "自然灰2", - thumbnail: "https://xximg1.meitudata.com/OXOH0zayv2.png!thumb-w321-webp75", - material_id: "20079981854" - }, - { - name: "自然灰1", - thumbnail: "https://xximg1.meitudata.com/mbeUyRpobN.png!thumb-w321-webp75", - material_id: "20079981853" - }, - { - name: "色散", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/3YDhOe5eO9.png!thumb-w321-webp75", - material_id: "20079981726" - }, - { - name: "毕业季", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/4VQt9N8Pjo.png!thumb-w321-webp75", - material_id: "20079981254" - }, - { - name: "一键美颜质感", - thumbnail: "https://xximg1.meitudata.com/x5NFQPRBWG.png!thumb-w321-webp75", - material_id: "20079982147" - }, - { - name: "相框吸色", - thumbnail: "https://xximg1.meitudata.com/2zmU1vvaPD.png!thumb-w321-webp75", - material_id: "20079982037" - }, - { - name: "美化吸色灰度限制", - thumbnail: "https://xximg1.meitudata.com/EPBcLQoodX.png!thumb-w321-webp75", - material_id: "20079980037" - }, - { - name: "相机吸色测试", - thumbnail: "https://xximg1.meitudata.com/p0LTZqZ2Wm.jpeg!thumb-w321-webp75", - material_id: "20079981873" - }, - { - name: "吸色测试2", - thumbnail: "https://xximg1.meitudata.com/9nzUyoDvEQ.png!thumb-w321-webp75", - material_id: "20079981874" - }, - { - name: "吸色灰度限制0.5", - thumbnail: "https://xximg1.meitudata.com/9nzUyYZ5LZ.png!thumb-w321-webp75", - material_id: "20079981029" - }, - { - name: "foodie锐化", - thumbnail: "https://xximg1.meitudata.com/oJWHn2klo6.png!thumb-w321-webp75", - material_id: "20079982000" - }, - { - name: "自然风光", - thumbnail: "https://xximg1.meitudata.com/N8eUq1aDk1.jpeg!thumb-w321-webp75", - material_id: "20079982003" - }, - { - name: "室内", - thumbnail: "https://xximg1.meitudata.com/LEasJPl8mE.jpeg!thumb-w321-webp75", - material_id: "20079982004" - }, - { - name: "室外", - thumbnail: "https://xximg1.meitudata.com/g5eFOyEePx.jpeg!thumb-w321-webp75", - material_id: "20079982005" - }, - { - name: "老人", - thumbnail: "https://xximg1.meitudata.com/vgzUl2EGV5.jpeg!thumb-w321-webp75", - material_id: "20079982006" - }, - { - name: "街道复用", - thumbnail: "https://xximg1.meitudata.com/QEasmNjL8x.jpeg!thumb-w321-webp75", - material_id: "20079981781" - }, - { - name: "大师1", - thumbnail: "https://xximg1.meitudata.com/wgNU30boQ4.png!thumb-w321-webp75", - material_id: "20079981962" - }, - { - name: "大师2", - thumbnail: "https://xximg1.meitudata.com/YkpU1yPmPa.png!thumb-w321-webp75", - material_id: "20079981963" - }, - { - name: "大师3", - thumbnail: "https://xximg1.meitudata.com/qRdTJOZYn8.png!thumb-w321-webp75", - material_id: "20079981964" - }, - { - name: "大师4", - thumbnail: "https://xximg1.meitudata.com/LEasJoKld4.png!thumb-w321-webp75", - material_id: "20079981965" - }, - { - name: "大师5", - thumbnail: "https://xximg1.meitudata.com/KeVUaQnzez.png!thumb-w321-webp75", - material_id: "20079981966" - }, - { - name: "大师6", - thumbnail: "https://xximg1.meitudata.com/BNGiP6vklD.png!thumb-w321-webp75", - material_id: "20079981967" - }, - { - name: "大师7", - thumbnail: "https://xximg1.meitudata.com/wgNU30boLG.png!thumb-w321-webp75", - material_id: "20079981968" - }, - { - name: "大师8", - thumbnail: "https://xximg1.meitudata.com/j4Gce54yGg.png!thumb-w321-webp75", - material_id: "20079981969" - }, - { - name: "大师9", - thumbnail: "https://xximg1.meitudata.com/g5eFO8dEYO.png!thumb-w321-webp75", - material_id: "20079981970" - }, - { - name: "模糊相机测试", - thumbnail: "https://xximg1.meitudata.com/wgNU3GgL0p.png!thumb-w321-webp75", - material_id: "20079982016" - }, - { - name: "", - thumbnail: "https://xximg1.meitudata.com/g5eFOoOWnK.png!thumb-w321-webp75", - material_id: "20079981872" - }, - { - name: "复古", - thumbnail: "https://xximg1.meitudata.com/x5NFQGlPeD.png!thumb-w321-webp75", - material_id: "20079982026" - }, - { - name: "夜景抠图", - thumbnail: "https://xximg1.meitudata.com/qRdTJOxoK4.png!thumb-w321-webp75", - material_id: "20079981936" - }, - { - name: "我爱中国", - thumbnail: "https://xximg1.meitudata.com/3YDhOJgLgp.png!thumb-w321-webp75", - material_id: "20079981422" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/oJQiW3WX68vDyaW.jpeg!thumb-w321-webp75", - material_id: "20079992271" - }, - { - name: "桃子奶油", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/lk0HjR5ealbk2Ze.jpeg!thumb-w321-webp75", - material_id: "20079992275" - }, - { - name: "橘粉奶油", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/5JOi8ZVNnqE8w8g.jpeg!thumb-w321-webp75", - material_id: "20079990090" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/DyOHJnqNb5o8LQg.jpeg!thumb-w321-webp75", - material_id: "20079992277" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/y59IlX8wQXoNGEK.jpeg!thumb-w321-webp75", - material_id: "20079992276" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/dWku02V3BpYDylY.jpeg!thumb-w321-webp75", - material_id: "20079990081" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/p0GcOvOamLXoD3Z.jpeg!thumb-w321-webp75", - material_id: "20079992273" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/6kOHqmQ3qjPoYQa.jpeg!thumb-w321-webp75", - material_id: "20079992266" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/RwXHJpZKvlQK2GJ.jpeg!thumb-w321-webp75", - material_id: "20079992267" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/2zVHE2XGb3d0xgv.jpeg!thumb-w321-webp75", - material_id: "20079990045" - }, - { - name: "炫光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/dWku0XJZxGgDLO9.jpeg!thumb-w321-webp75", - material_id: "20079992229" - }, - { - name: "CG3", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/JdXHB6LLY8agV8O.jpeg!thumb-w321-webp75", - material_id: "20079992238" - }, - { - name: "CG测试2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/j4QUVv66nq0P55z.jpeg!thumb-w321-webp75", - material_id: "20079991111" - }, - { - name: "国庆挂历2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/YkVHj0oP36pmOL1.jpeg!thumb-w321-webp75", - material_id: "20079999999" - }, - { - name: "美化国庆挂历", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/g5yIKQJdqK1aBlQ.jpeg!thumb-w321-webp75", - material_id: "20079998888" - }, - { - name: "在逃公主", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/N8RHzY8o4oKQqEJ.jpeg!thumb-w321-webp75", - material_id: "20079992200" - }, - { - name: "2020美颜默认", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/qR1tRvwgl5mmZDp.jpeg!thumb-w321-webp75", - material_id: "20079990088" - }, - { - name: "复古画册", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/zq9HXROgvWQpXJp.jpeg!thumb-w321-webp75", - material_id: "20079990080" - }, - { - name: "美食1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/PBXUkeGKl4.jpeg!thumb-w321-webp75", - material_id: "20079990060" - }, - { - name: "美食改", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8QYIwP5P0x.jpeg!thumb-w321-webp75", - material_id: "20079990059" - }, - { - name: "细细闪", - thumbnail: "https://xximg1.meitudata.com/XeVHPQGmWg.jpeg!thumb-w321-webp75", - material_id: "20079991001" - }, - { - name: "细细闪2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/qR1tRvRVBLmGaQK.jpeg!thumb-w321-webp75", - material_id: "20079990069" - }, - { - name: "新闪闪1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/aOWsGzGNdEOkngB.jpeg!thumb-w321-webp75", - material_id: "20079990068" - }, - { - name: "新闪闪3-2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/qR1tRvbq9BkKZZv.jpeg!thumb-w321-webp75", - material_id: "20079990078" - }, - { - name: "新闪闪5", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/j4QUVwNX4q6LV80.jpeg!thumb-w321-webp75", - material_id: "20079990077" - }, - { - name: "天空闪-橙", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/wgRHvqkwVNgzaQO.jpeg!thumb-w321-webp75", - material_id: "20079990073" - }, - { - name: "新说3", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/6kOHRdweoL.jpeg!thumb-w321-webp75", - material_id: "20079990057" - }, - { - name: "新说2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/QEXSmQ3JNP.jpeg!thumb-w321-webp75", - material_id: "20079990056" - }, - { - name: "新说", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/nBqUo2nYVw.jpeg!thumb-w321-webp75", - material_id: "20079990055" - }, - { - name: "少女胶片颗粒", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/EPdUL929P1.jpeg!thumb-w321-webp75", - material_id: "20079990053" - }, - { - name: "折纸3", - thumbnail: "https://xximg1.meitudata.com/j4QUeqxb8N.jpeg!thumb-w321-webp75", - material_id: "20079990021" - }, - { - name: "折纸滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/dWkuagV42R.jpeg!thumb-w321-webp75", - material_id: "20079990050" - }, - { - name: "温柔纹理2", - thumbnail: "https://xximg1.meitudata.com/5JOiDgbOjz.jpeg!thumb-w321-webp75", - material_id: "20079990003" - }, - { - name: "温柔纹理", - thumbnail: "https://xximg1.meitudata.com/mbZHy9Kyd4.jpeg!thumb-w321-webp75", - material_id: "20079990024" - }, - { - name: "油画新说1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/dWkuaga464.jpeg!thumb-w321-webp75", - material_id: "20079990054" - }, - { - name: "油画新说2", - thumbnail: "https://xximg1.meitudata.com/V2jTkpeb8e.jpeg!thumb-w321-webp75", - material_id: "20079990001" - }, - { - name: "油画新说3", - thumbnail: "https://xximg1.meitudata.com/8QYIw15O85.jpeg!thumb-w321-webp75", - material_id: "20079991994" - }, - { - name: "珍珠2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/zq9H5VqpGo.jpeg!thumb-w321-webp75", - material_id: "20079990043" - }, - { - name: "珍珠", - thumbnail: "https://xximg1.meitudata.com/dWkua4m2NL.jpeg!thumb-w321-webp75", - material_id: "20079991002" - }, - { - name: "爱心月亮", - thumbnail: "https://xximg1.meitudata.com/LEKSJEmG9L.png!thumb-w321-webp75", - material_id: "20079990041" - }, - { - name: "星星月亮", - thumbnail: "https://xximg1.meitudata.com/3YVfOV8JWX.jpeg!thumb-w321-webp75", - material_id: "20079990040" - }, - { - name: "钢笔淡彩", - thumbnail: "https://xximg1.meitudata.com/6kOHRlg2E8.jpeg!thumb-w321-webp75", - material_id: "20079990039" - }, - { - name: "拯救废片2", - thumbnail: "https://xximg1.meitudata.com/KeEHax9bao.jpeg!thumb-w321-webp75", - material_id: "20079990038" - }, - { - name: "书本感", - thumbnail: "https://xximg1.meitudata.com/WYBfmxXLVz.jpeg!thumb-w321-webp75", - material_id: "20079990036" - }, - { - name: "拯救废片1", - thumbnail: "https://xximg1.meitudata.com/dWkuaGDnaG.jpeg!thumb-w321-webp75", - material_id: "20079990037" - }, - { - name: "夏日4", - thumbnail: "https://xximg1.meitudata.com/j4QUeglDGW.jpeg!thumb-w321-webp75", - material_id: "20079990035" - }, - { - name: "夏日2", - thumbnail: "https://xximg1.meitudata.com/zq9H5obmPq.jpeg!thumb-w321-webp75", - material_id: "20079990034" - }, - { - name: "夏日3", - thumbnail: "https://xximg1.meitudata.com/lk0Hk4z4JE.jpeg!thumb-w321-webp75", - material_id: "20079990033" - }, - { - name: "春日柔光", - thumbnail: "https://xximg1.meitudata.com/oJQinkgLpN.jpeg!thumb-w321-webp75", - material_id: "20079992020" - }, - { - name: "春日日系", - thumbnail: "https://xximg1.meitudata.com/JdXHWgQ588.jpeg!thumb-w321-webp75", - material_id: "20079990022" - }, - { - name: "2020情人节2", - thumbnail: "https://xximg1.meitudata.com/p0GcZg6o2m.jpeg!thumb-w321-webp75", - material_id: "20079990017" - }, - { - name: "11", - thumbnail: "https://xximg1.meitudata.com/j4QUeR0ylz.jpeg!thumb-w321-webp75", - material_id: "20079990014" - }, - { - name: "锐化+颗粒", - thumbnail: "https://xximg1.meitudata.com/nBqUoWm5Eq.jpeg!thumb-w321-webp75", - material_id: "20079990015" - }, - { - name: "22", - thumbnail: "https://xximg1.meitudata.com/EPdULoXbK8.jpeg!thumb-w321-webp75", - material_id: "20079990004" - }, - { - name: "2019圣诞2", - thumbnail: "https://xximg1.meitudata.com/aOWsL0gy9L.jpeg!thumb-w321-webp75", - material_id: "20079990008" - }, - { - name: "bling裸妆", - thumbnail: "https://xximg1.meitudata.com/Z3Vspe2qwl.jpeg!thumb-w321-webp75", - material_id: "20079991976" - }, - { - name: "水雾", - thumbnail: "https://xximg1.meitudata.com/eqOHv8b9Jy.jpeg!thumb-w321-webp75", - material_id: "20079990009" - }, - { - name: "泫雅柔光2", - thumbnail: "https://xximg1.meitudata.com/1zVH1X80BW.jpeg!thumb-w321-webp75", - material_id: "20079990000" - }, - { - name: "无辜妆", - thumbnail: "https://xximg1.meitudata.com/8QYIw5OwzQ.jpeg!thumb-w321-webp75", - material_id: "20079990020" - }, - { - name: "2023", - thumbnail: "https://xximg1.meitudata.com/WYBfmyJ4jO.jpeg!thumb-w321-webp75", - material_id: "20079992023" - }, - { - name: "风格妆22", - thumbnail: "https://xximg1.meitudata.com/y59IbNv8ek.jpeg!thumb-w321-webp75", - material_id: "20079990013" - }, - { - name: "吸色测试", - thumbnail: "https://xximg1.meitudata.com/g5yIOn36zo.jpeg!thumb-w321-webp75", - material_id: "20079991234" - }, - { - name: "爱国", - thumbnail: "https://xximg1.meitudata.com/nBqUonb8dG.jpeg!thumb-w321-webp75", - material_id: "20079991919" - }, - { - name: "春樱改良", - thumbnail: "https://xximg1.meitudata.com/2zVH1mYp8m.jpeg!thumb-w321-webp75", - material_id: "20079991858" - }, - { - name: "爱心阴影", - thumbnail: "https://xximg1.meitudata.com/8QYIw59nPj.jpeg!thumb-w321-webp75", - material_id: "20079991935" - }, - { - name: "无辜妆2", - thumbnail: "https://xximg1.meitudata.com/LEKSJPXj2E.jpeg!thumb-w321-webp75", - material_id: "20079990006" - }, - { - name: "1993", - thumbnail: "https://xximg1.meitudata.com/vgyHlz3kOv.jpeg!thumb-w321-webp75", - material_id: "20079991993" - }, - { - name: "皮肤质感3", - thumbnail: "https://xximg1.meitudata.com/LEKSJooQb8.jpeg!thumb-w321-webp75", - material_id: "20079991113" - }, - { - name: "皮肤质感2", - thumbnail: "https://xximg1.meitudata.com/p0GcZPPXNW.jpeg!thumb-w321-webp75", - material_id: "20079991112" - }, - { - name: "皮肤质感", - thumbnail: "https://xximg1.meitudata.com/WYBfmDvxQK.jpeg!thumb-w321-webp75", - material_id: "20079991884" - }, - { - name: "万圣2", - thumbnail: "https://xximg1.meitudata.com/dWkuap6DpQ.jpeg!thumb-w321-webp75", - material_id: "20079991866" - }, - { - name: "闪闪压缩2", - thumbnail: "https://xximg1.meitudata.com/BNPTP2XB6X.jpeg!thumb-w321-webp75", - material_id: "20079990030" - }, - { - name: "闪闪压缩", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/oJQiWKQQE3VWLKR.jpeg!thumb-w321-webp75", - material_id: "20079990076" - }, - { - name: "复古画册加纸纹", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/QEXSyJENWDbBkna.jpeg!thumb-w321-webp75", - material_id: "20079990079" - }, - { - name: "新闪闪2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/KeEHpDpzvPgVOqY.jpeg!thumb-w321-webp75", - material_id: "20079990067" - }, - { - name: "新闪闪4", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/mbZHO5dyaXPe91g.jpeg!thumb-w321-webp75", - material_id: "20079990072" - }, - { - name: "天空闪", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/bBLUYpzJY518dqa.jpeg!thumb-w321-webp75", - material_id: "20079990070" - }, - { - name: "美食3改", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/RwXHmlPzDO.jpeg!thumb-w321-webp75", - material_id: "20079990058" - }, - { - name: "质感胶片颗粒", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/1zVH1pkp0o.jpeg!thumb-w321-webp75", - material_id: "20079990052" - }, - { - name: "雀斑", - thumbnail: "https://xximg1.meitudata.com/bBLUvke8mb.jpeg!thumb-w321-webp75", - material_id: "20079991000" - }, - { - name: "春日柔光1", - thumbnail: "https://xximg1.meitudata.com/KeEHa9ZGYO.jpeg!thumb-w321-webp75", - material_id: "20079990031" - }, - { - name: "灰调2", - thumbnail: "https://xximg1.meitudata.com/JdXHW51qgp.jpeg!thumb-w321-webp75", - material_id: "20079990002" - }, - { - name: "移轴虚化2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/WYBf3WkazQXvlwl.jpeg!thumb-w321-webp75", - material_id: "506000008" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/6kOHqmdRWjl4BEm.jpeg!thumb-w321-webp75", - material_id: "50600006" - }, - { - name: "微光", - thumbnail: "https://xximg1.meitudata.com/KeEHa4wl8p.jpeg!thumb-w321-webp75", - material_id: "20076251574" - }, - { - name: "微光", - thumbnail: "https://xximg1.meitudata.com/eqZUvNY3YL.png!thumb-w321-webp75", - material_id: "20076251567" - }, - { - name: "微光", - thumbnail: "https://xximg1.meitudata.com/BNPTPoVbeV.jpeg!thumb-w321-webp75", - material_id: "20076251575" - }, - { - name: "微光", - thumbnail: "https://xximg1.meitudata.com/bBZcvmqk8z.png!thumb-w321-webp75", - material_id: "20076251568" - }, - { - name: "微光", - thumbnail: "https://xximg1.meitudata.com/g5yIOPJmZn.jpeg!thumb-w321-webp75", - material_id: "20076251573" - }, - { - name: "微光", - thumbnail: "https://xximg1.meitudata.com/5JeHDVNROj.png!thumb-w321-webp75", - material_id: "20076251569" - }, - { - name: "微光", - thumbnail: "https://xximg1.meitudata.com/wgNU3PJVzR.png!thumb-w321-webp75", - material_id: "20076251577" - }, - { - name: "17.素材无人脸规则", - thumbnail: "https://xxtool-release.zone1.meitudata.com/owv1PzKKkaEz.jpg!thumb-w321-webp75", - material_id: "506088832" - }, - { - name: "22.天空分割滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/laM7B2WRZRve.jpg!thumb-w321-webp75", - material_id: "506088818" - }, - { - name: "11.柔发效果支持", - thumbnail: "https://xxtool-release.zone1.meitudata.com/L24ZJRgYjyw5.png!thumb-w321-webp75", - material_id: "506088812" - }, - { - name: "13.3D打光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/owv1PzXYWoxL.png!thumb-w321-webp75", - material_id: "506088810" - }, - { - name: "(仅日本)10.不带妆容", - thumbnail: "https://xxtool-release.zone1.meitudata.com/y1B45rOXGkBW.jpeg!thumb-w321-webp75", - material_id: "506088811" - }, - { - name: "12.妆容滤镜层级控制1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/e4xlJA94A6Mp.png!thumb-w321-webp75", - material_id: "506088831" - }, - { - name: "6.强制曝光滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/kOo1k0DW9drL.jpg!thumb-w321-webp75", - material_id: "506088835" - }, - { - name: "5.男生、女生滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/OmB4J1oagj0M.jpg!thumb-w321-webp75", - material_id: "506088815" - }, - { - name: "4.带有动效滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/V06dJl3051WA.jpg!thumb-w321-webp75", - material_id: "506088823" - }, - { - name: "12.妆容滤镜层级控制", - thumbnail: "https://xxtool-release.zone1.meitudata.com/kOo1k0Nva6LL.png!thumb-w321-webp75", - material_id: "506088816" - }, - { - name: "3.妆容滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/OmB4J13jO5Dg.jpg!thumb-w321-webp75", - material_id: "506088822" - }, - { - name: "2.bling滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/e4xlJA9WZJoy.jpg!thumb-w321-webp75", - material_id: "506088834" - }, - { - name: "1.随机滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/GYZza015D1LE.jpg!thumb-w321-webp75", - material_id: "506088824" - }, - { - name: "19.素材多比例滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/B6MlzDxYm10N.jpg!thumb-w321-webp75", - material_id: "506088821" - }, - { - name: "16.美食手绘效果", - thumbnail: "https://xxtool-release.zone1.meitudata.com/4JpRm4Em6Y7J.jpg!thumb-w321-webp75", - material_id: "506088833" - }, - { - name: "16.美食手绘效果2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/3zVRNe69zMKM.jpg!thumb-w321-webp75", - material_id: "506088829" - }, - { - name: "23.妆容滤镜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/L24ZJRgjNYWe.jpg!thumb-w321-webp75", - material_id: "506088814" - }, - { - name: "18.径向色散效果", - thumbnail: "https://xxtool-release.zone1.meitudata.com/y1B45rOEZeMP.jpg!thumb-w321-webp75", - material_id: "506088813" - }, - { - name: "14.新bling效果", - thumbnail: "https://xxtool-release.zone1.meitudata.com/y1B45rp8Ky7z.png!thumb-w321-webp75", - material_id: "506088837" - }, - { - name: "绵绵", - thumbnail: "https://xximg1.meitudata.com/EPdUL42bLj.jpeg!thumb-w321-webp75", - material_id: "20076412020" - }, - { - name: "清澈", - thumbnail: "https://xximg1.meitudata.com/nBqUo6pveO.jpeg!thumb-w321-webp75", - material_id: "20076412057" - }, - { - name: "阴雨", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/N3OFzPJbwBgXG6z.jpeg!thumb-w321-webp75", - material_id: "506002342" - }, - { - name: "ar胶片", - thumbnail: "https://xximg1.meitudata.com/lkjUk85p5G.png!thumb-w321-webp75", - material_id: "20108891781" - }, - { - name: "INS", - thumbnail: "https://xximg1.meitudata.com/bBLUv93q2O.jpeg!thumb-w321-webp75", - material_id: "20108890012" - }, - { - name: "高级灰", - thumbnail: "https://xximg1.meitudata.com/jzOIeJz1mK.png!thumb-w321-webp75", - material_id: "20108891777" - }, - { - name: "夜景2", - thumbnail: "https://xximg1.meitudata.com/1zVH1nl3nL.jpeg!thumb-w321-webp75", - material_id: "20108890014" - }, - { - name: "藕色颗粒", - thumbnail: "https://xximg1.meitudata.com/V2jTkoQgO6.jpeg!thumb-w321-webp75", - material_id: "20108890015" - }, - { - name: "胶片1", - thumbnail: "https://xximg1.meitudata.com/KnJSa8gWE4.png!thumb-w321-webp75", - material_id: "20108891783" - }, - { - name: "fuji", - thumbnail: "https://xximg1.meitudata.com/qR1tJbvd5d.jpeg!thumb-w321-webp75", - material_id: "20108890010" - }, - { - name: "咸咸测试", - thumbnail: "https://xximg1.meitudata.com/EPdULqxXOR.jpeg!thumb-w321-webp75", - material_id: "20108899999" - }, - { - name: "古风藕色", - thumbnail: "https://xximg1.meitudata.com/JdXHWjK4o9.jpeg!thumb-w321-webp75", - material_id: "20108891775" - }, - { - name: "natural1", - thumbnail: "https://xximg1.meitudata.com/zq9H5ekDQv.jpeg!thumb-w321-webp75", - material_id: "20108891682" - }, - { - name: "受伤妆", - thumbnail: "https://xximg1.meitudata.com/nBqUoEKnoD.jpeg!thumb-w321-webp75", - material_id: "20108891787" - }, - { - name: "natural2", - thumbnail: "https://xximg1.meitudata.com/j4QUedKlNv.jpeg!thumb-w321-webp75", - material_id: "20108891848" - }, - { - name: "描边2", - thumbnail: "https://xximg1.meitudata.com/4VOC9QOZRd.jpeg!thumb-w321-webp75", - material_id: "20108891835" - }, - { - name: "natural3", - thumbnail: "https://xximg1.meitudata.com/9nOHy6gJOb.jpeg!thumb-w321-webp75", - material_id: "20108890019" - }, - { - name: "宝丽来1", - thumbnail: "https://xximg1.meitudata.com/JdXHW5qp88.jpeg!thumb-w321-webp75", - material_id: "20108891840" - }, - { - name: "日系复古", - thumbnail: "https://xximg1.meitudata.com/6zZIRWym8L.png!thumb-w321-webp75", - material_id: "20108891846" - }, - { - name: "暗调测试", - thumbnail: "https://xximg1.meitudata.com/YkVH1pNvLW.jpeg!thumb-w321-webp75", - material_id: "20108897777" - }, - { - name: "宝丽来2", - thumbnail: "https://xximg1.meitudata.com/6kOHRWaqeJ.jpeg!thumb-w321-webp75", - material_id: "20108891841" - }, - { - name: "海外柔光", - thumbnail: "https://xximg1.meitudata.com/p0GcZyqa5z.jpeg!thumb-w321-webp75", - material_id: "20108891801" - }, - { - name: "绯红", - thumbnail: "https://xximg1.meitudata.com/wgNU340lGP.png!thumb-w321-webp75", - material_id: "20108891770" - }, - { - name: "霜色", - thumbnail: "https://xximg1.meitudata.com/lkjUkL6GXp.png!thumb-w321-webp75", - material_id: "20108891769" - }, - { - name: "梅幸茶", - thumbnail: "https://xximg1.meitudata.com/GPWcPEw1Wj.png!thumb-w321-webp75", - material_id: "20108891771" - }, - { - name: "水墨", - thumbnail: "https://xximg1.meitudata.com/9nzUyjane8.png!thumb-w321-webp75", - material_id: "20108891772" - }, - { - name: "1780", - thumbnail: "https://xximg1.meitudata.com/vgyHlJoLg3.jpeg!thumb-w321-webp75", - material_id: "20108891780" - }, - { - name: "描边3", - thumbnail: "https://xximg1.meitudata.com/nBqUoz0g3y.jpeg!thumb-w321-webp75", - material_id: "20108891837" - }, - { - name: "描边4", - thumbnail: "https://xximg1.meitudata.com/1zVH1XvE4z.jpeg!thumb-w321-webp75", - material_id: "20108891838" - }, - { - name: "描边1", - thumbnail: "https://xximg1.meitudata.com/WYBfmdWOoQ.jpeg!thumb-w321-webp75", - material_id: "20108891834" - }, - { - name: "青空", - thumbnail: "https://xximg1.meitudata.com/8OkUwBympV.jpeg!thumb-w321-webp75", - material_id: "20106311844" - }, - { - name: "暮霭", - thumbnail: "https://xximg1.meitudata.com/9QktyzB0nW.jpeg!thumb-w321-webp75", - material_id: "20106311843" - }, - { - name: "糖果", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/qp4IReJkzbEZD41.jpeg!thumb-w321-webp75", - material_id: "506002348" - }, - { - name: "苏打", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/nplIo2XOm6.jpeg!thumb-w321-webp75", - material_id: "20096442168" - }, - { - name: "淡奶油", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/PVQIkeOx5L.jpeg!thumb-w321-webp75", - material_id: "20096442169" - }, - { - name: "温柔", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/N8RHqBW392.jpeg!thumb-w321-webp75", - material_id: "20096442166" - }, - { - name: "珠光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/nBqUo2X8lP.jpeg!thumb-w321-webp75", - material_id: "20096442165" - }, - { - name: "丝绸", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/BNPTPWpNRL.jpeg!thumb-w321-webp75", - material_id: "20096442167" - }, - { - name: "黑白", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/OXOHRJnJ5vOXBpY.jpeg!thumb-w321-webp75", - material_id: "20096081095" - }, - { - name: "暗房", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/eqZU6dBdw3EY3d1.jpeg!thumb-w321-webp75", - material_id: "506002307" - }, - { - name: "低反差", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/4VQtnwmwpBL5wDN.jpeg!thumb-w321-webp75", - material_id: "506002309" - }, - { - name: "森山", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/1zXUmEWE4DWbQW2.jpeg!thumb-w321-webp75", - material_id: "506002313" - }, - { - name: "Instant", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/qRdTR9w9ygLqdeb.jpeg!thumb-w321-webp75", - material_id: "506002310" - }, - { - name: "范戴克", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/BNGiqXNXZkljKlb.jpeg!thumb-w321-webp75", - material_id: "506002312" - }, - { - name: "圣诞", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/PVQIdno4bnlEEOq.jpeg!thumb-w321-webp75", - material_id: "20076452266" - }, - { - name: "小梦境", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/2zVHEkJKPj3q5nz.jpeg!thumb-w321-webp75", - material_id: "20076452193" - }, - { - name: "在逃公主", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/lk0HjoJRKlq9E0Z.jpeg!thumb-w321-webp75", - material_id: "20076452200" - }, - { - name: "璀璨", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/DyOHJWjXJGk6Ojl.jpeg!thumb-w321-webp75", - material_id: "20076452189" - }, - { - name: "星河", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/LEKSjB8pREZExxZ.jpeg!thumb-w321-webp75", - material_id: "20076452180" - }, - { - name: "星河II", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/g5yIKQq99XvKZQP.jpeg!thumb-w321-webp75", - material_id: "20076452181" - }, - { - name: "星河III", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/oJQiWKVVJNXe3PB.jpeg!thumb-w321-webp75", - material_id: "20076452184" - }, - { - name: "年味", - thumbnail: "https://xximg1.meitudata.com/EVvILvqjRQ.jpeg!thumb-w321-webp75", - material_id: "20076352017" - }, - { - name: "白日梦 ", - thumbnail: "https://xximg1.meitudata.com/vgyHlzq34j.jpeg!thumb-w321-webp75", - material_id: "20076351989" - }, - { - name: "白桃", - thumbnail: "https://xximg1.meitudata.com/g5eFOnEgJj.png!thumb-w321-webp75", - material_id: "20076351888" - }, - { - name: "Norge", - thumbnail: "https://xximg1.meitudata.com/9nOHylkdjn.jpeg!thumb-w321-webp75", - material_id: "2007635584" - }, - { - name: "冬至", - thumbnail: "https://xximg1.meitudata.com/zqOU5K9Q5m.png!thumb-w321-webp75", - material_id: "20076351887" - }, - { - name: "红杉", - thumbnail: "https://xximg1.meitudata.com/QEasmkjv09.png!thumb-w321-webp75", - material_id: "20076351981" - }, - { - name: "暖暖 ", - thumbnail: "https://xximg1.meitudata.com/y59Ib9kQZw.jpeg!thumb-w321-webp75", - material_id: "20076351988" - }, - { - name: "神仙奶油光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/XRLI50zwLEBpQ19.jpeg!thumb-w321-webp75", - material_id: "20096472176" - }, - { - name: "神仙奶油光Ⅱ", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/PVQIdB3VbL63GQN.jpeg!thumb-w321-webp75", - material_id: "20096472177" - }, - { - name: "法式油画", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/99ETGwRbEnKn9qd.jpeg!thumb-w321-webp75", - material_id: "20096472178" - }, - { - name: "法式油画Ⅱ", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/wkQsvqJNVn9GaaP.jpeg!thumb-w321-webp75", - material_id: "20096472179" - }, - { - name: "曼哈顿大桥", - thumbnail: "https://xximg1.meitudata.com/bBLUvjadBB.jpeg!thumb-w321-webp75", - material_id: "20106301861" - }, - { - name: "维多利亚港", - thumbnail: "https://xximg1.meitudata.com/wgRH3jVEDg.jpeg!thumb-w321-webp75", - material_id: "20106301862" - }, - { - name: "新宿", - thumbnail: "https://xximg1.meitudata.com/x5eIQyn1bp.jpeg!thumb-w321-webp75", - material_id: "20106301935" - }, - { - name: "那不勒斯", - thumbnail: "https://xximg1.meitudata.com/zqOU5LPDwe.png!thumb-w321-webp75", - material_id: "20106301936" - }, - { - name: "赛博朋克", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/ykGslXOBVOZ1wZm.jpeg!thumb-w321-webp75", - material_id: "20106302217" - }, - { - name: "盐系", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/29gtLmJEWLAadDk.jpeg!thumb-w321-webp75", - material_id: "20096522258" - }, - { - name: "29", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/wOXiNM4O8Jxyv7E.jpeg!thumb-w321-webp75", - material_id: "506030029" - }, - { - name: "28", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/jkEIPyplzRPYXmP.jpeg!thumb-w321-webp75", - material_id: "506030028" - }, - { - name: "27", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/JgpCWo3NL2G81JZ.jpeg!thumb-w321-webp75", - material_id: "506030027" - }, - { - name: "26", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/DpWhZ42RzkXpDgW.jpeg!thumb-w321-webp75", - material_id: "5060230026" - }, - { - name: "25", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/Ng9CWAVaM342ERY.jpeg!thumb-w321-webp75", - material_id: "506030025" - }, - { - name: "24", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/3MLhOxa9x2m49Rp.jpeg!thumb-w321-webp75", - material_id: "506030024" - }, - { - name: "23", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/Ng9CWAVy999Ea27.jpeg!thumb-w321-webp75", - material_id: "506030023" - }, - { - name: "22", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/mjkuw39egW2Lv9V.jpeg!thumb-w321-webp75", - material_id: "506030022" - }, - { - name: "21", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/9L1IrA1E6l7799X.jpeg!thumb-w321-webp75", - material_id: "506030021" - }, - { - name: "20", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/rOwi0AkzJo210E5.jpeg!thumb-w321-webp75", - material_id: "506030020" - }, - { - name: "19", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/ed7IAoW6mr4OVwl.jpeg!thumb-w321-webp75", - material_id: "506030019" - }, - { - name: "18", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/3MLhOx2A2R5GJom.jpeg!thumb-w321-webp75", - material_id: "506030018" - }, - { - name: "17", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/DpWhZ4mE9OZYEv2.jpeg!thumb-w321-webp75", - material_id: "506030017" - }, - { - name: "16", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/lK4H8LpVG7o8ZWo.jpeg!thumb-w321-webp75", - material_id: "506030016" - }, - { - name: "15", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/JgpCWoverVMGlGE.jpeg!thumb-w321-webp75", - material_id: "506030015" - }, - { - name: "14", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/rOwi0Akmj2Wrg8e.jpeg!thumb-w321-webp75", - material_id: "506030014" - }, - { - name: "13", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/ZWdUX61ERRREYx7.jpeg!thumb-w321-webp75", - material_id: "506030013" - }, - { - name: "12", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/Ng9CWAdDvZDZWN0.jpeg!thumb-w321-webp75", - material_id: "506030012" - }, - { - name: "10", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/5O0iJdZB9VKpZYK.jpeg!thumb-w321-webp75", - material_id: "506030006" - }, - { - name: "9", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/AY4T4jpeo29dAJY.jpeg!thumb-w321-webp75", - material_id: "506030005" - }, - { - name: "8", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/EZNHdk171m9Z1Zp.jpeg!thumb-w321-webp75", - material_id: "506030004" - }, - { - name: "7", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/ed7IAorRPGr2DV1.jpeg!thumb-w321-webp75", - material_id: "506030003" - }, - { - name: "测试红点6", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/wOXiNMwZDXGyaOW.jpeg!thumb-w321-webp75", - material_id: "506030002" - }, - { - name: "测试红点5", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/9L1IrAz4yYjKLOp.jpeg!thumb-w321-webp75", - material_id: "506030001" - }, - { - name: "测试红点4", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/EZNHdk1owgyDp9Z.jpeg!thumb-w321-webp75", - material_id: "5060238998" - }, - { - name: "测试红点3", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/3MLhOxDGxyVLwmV.jpeg!thumb-w321-webp75", - material_id: "5060238997" - }, - { - name: "测试红点2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/jkEIPyVeNwvW4X8.jpeg!thumb-w321-webp75", - material_id: "5060238996" - }, - { - name: "测试红点", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/3MLhOxDGZNlxerJ.jpeg!thumb-w321-webp75", - material_id: "5060238995" - }, - { - name: "123", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/8LxIzOLaGM06Ekx.jpeg!thumb-w321-webp75", - material_id: "5060238991" - }, - { - name: "小草3", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/09mIW9BGz4DdNPK.jpeg!thumb-w321-webp75", - material_id: "5060238990" - }, - { - name: "小草2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/wOXiNMPBxwoymBv.jpeg!thumb-w321-webp75", - material_id: "5060238989" - }, - { - name: "小草", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/rOwi0AO9BdZzzkv.jpeg!thumb-w321-webp75", - material_id: "5060238988" - }, - { - name: "星星1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/71zSrY2veyMDEaE.jpeg!thumb-w321-webp75", - material_id: "506010255" - }, - { - name: "小树1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/4AVSv70z5YzveBy.jpeg!thumb-w321-webp75", - material_id: "506010256" - }, - { - name: "小花4", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/G5wUz1B4zNpRE0P.jpeg!thumb-w321-webp75", - material_id: "506010254" - }, - { - name: "31", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/Kg2CJr4d5PKRP4p.jpeg!thumb-w321-webp75", - material_id: "506030031" - }, - { - name: "西西里", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/yNmTl3VBD1Nz5yE.jpeg!thumb-w321-webp75", - material_id: "506002337" - }, - { - name: "尼斯", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/5JeH8E3e6Vvq8YY.jpeg!thumb-w321-webp75", - material_id: "506002331" - }, - { - name: "布达佩斯", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/lkjUjRxbeV2moG0.jpeg!thumb-w321-webp75", - material_id: "20096082224" - }, - { - name: "月升", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/qRdTRadQlQanwQa.jpeg!thumb-w321-webp75", - material_id: "20096082225" - }, - { - name: "聚会", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/5JeH8ZOg14O3Jq6.jpeg!thumb-w321-webp75", - material_id: "20096082226" - }, - { - name: "法兰西", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8QzFJz90eZ3lExp.jpeg!thumb-w321-webp75", - material_id: "20096082227" - }, - { - name: "油画", - thumbnail: "https://xximg1.meitudata.com/BNPTPRjbyl.jpeg!thumb-w321-webp75", - material_id: "20096082032" - }, - { - name: "油画Ⅱ", - thumbnail: "https://xximg1.meitudata.com/8QYIw41QVq.jpeg!thumb-w321-webp75", - material_id: "20096082033" - }, - { - name: "安东尼亚", - thumbnail: "https://xximg1.meitudata.com/2LqS1vmqv0.png!thumb-w321-webp75", - material_id: "20096082038" - }, - { - name: "情人节限定", - thumbnail: "https://xximg1.meitudata.com/8QYIwbzm81.jpeg!thumb-w321-webp75", - material_id: "20096082019" - }, - { - name: "勒芒", - thumbnail: "https://xximg1.meitudata.com/ndzuolR3v9.jpeg!thumb-w321-webp75", - material_id: "20096081869" - }, - { - name: "电影", - thumbnail: "https://xximg1.meitudata.com/PBXUkm2yq5.jpeg!thumb-w321-webp75", - material_id: "20096081274" - }, - { - name: "1980", - thumbnail: "http://xximg2.meitudata.com/LEKSJx0N8y.jpeg!thumb-w321-webp75", - material_id: "20096081108" - }, - { - name: "电影", - thumbnail: "http://xximg2.meitudata.com/6kOHRa1OpD.jpeg!thumb-w321-webp75", - material_id: "20096081253" - }, - { - name: "中环码头", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/496TnZaRxddxonY.jpeg!thumb-w321-webp75", - material_id: "2009608530" - }, - { - name: "显影", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8QYIJLL6NE94K2l.jpeg!thumb-w321-webp75", - material_id: "20086391964" - }, - { - name: "焦点", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8QYIJLL6o5PVGyv.jpeg!thumb-w321-webp75", - material_id: "20086391965" - }, - { - name: "扫街", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/5JOi8ooNJ25wpg2.jpeg!thumb-w321-webp75", - material_id: "20086391963" - }, - { - name: "Photog", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/OX8iREk4LJ3qgad.jpeg!thumb-w321-webp75", - material_id: "20086391968" - }, - { - name: "超焦距", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/5JOi8ZQeZkvzQQD.jpeg!thumb-w321-webp75", - material_id: "20086391970" - }, - { - name: "影调", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/XeVH5bx1bz4NVdk.jpeg!thumb-w321-webp75", - material_id: "20086391967" - }, - { - name: "名称一", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/MKosgAWo5VKpo9Z.jpeg!thumb-w321-webp75", - material_id: "50600999" - }, - { - name: "修改测试", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/69MiA9VxKdkzjWR.png!thumb-w321-webp75", - material_id: "50600229" - }, - { - name: "有门槛的我", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/P98i3gdgDw3Rmx5.png!thumb-w321-webp75", - material_id: "506093422" - }, - { - name: "222嗯嗯1", - thumbnail: "https://stage.meitudata.com/public/creator/ed70a3c696c8e7b.jpg!thumb-w321-webp75", - material_id: "50693244" - }, - { - name: "抠图批量拼图", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/pA2S9lvNpMY4X3N.jpeg!thumb-w321-webp75", - material_id: "506002368" - }, - { - name: "滤镜流程11", - thumbnail: "https://material-center-pre.meitudata.com/material/image/6194a57b5b9ef7678.jpg!thumb-w321-webp75", - material_id: "506017292" - }, - { - name: "月辉", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/wOXiBYpWgxA7ej5.jpeg!thumb-w321-webp75", - material_id: "5060112313" - }, - { - name: "旧-美食手绘1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/0lZCW9BrDgMroLL.jpeg!thumb-w321-webp75", - material_id: "5060002001" - }, - { - name: "hsxTest11.8", - thumbnail: "https://material-center-pre.meitudata.com/material/image/6189f29b48f165954.jpg!thumb-w321-webp75", - material_id: "506530963" - }, - { - name: "GT G", - thumbnail: "http://xximg2.meitudata.com/Z9DCJpkB2l.jpeg!thumb-w321-webp75", - material_id: "20085081215" - }, - { - name: "自然", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/j4QUVvP9GPkJ4n3.jpeg!thumb-w321-webp75", - material_id: "20076051317" - }, - { - name: "自然", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/5JOi8o1nKlqXmb2.jpeg!thumb-w321-webp75", - material_id: "20076051233" - }, - { - name: "Room", - thumbnail: "https://xximg1.meitudata.com/WYphm4QnOx.jpeg!thumb-w321-webp75", - material_id: "20076052148" - }, - { - name: "模糊", - thumbnail: "https://xximg1.meitudata.com/qR1tJmaVZg.jpeg!thumb-w321-webp75", - material_id: "20076052016" - }, - { - name: "追光", - thumbnail: "https://xximg1.meitudata.com/mbZHyl6E8Y.jpeg!thumb-w321-webp75", - material_id: "20076051714" - }, - { - name: "春樱", - thumbnail: "https://xximg1.meitudata.com/OX8i0kV1vG.jpeg!thumb-w321-webp75", - material_id: "20076051634" - }, - { - name: "夏橙", - thumbnail: "https://xximg1.meitudata.com/YkVH1doZzv.jpeg!thumb-w321-webp75", - material_id: "20076051635" - }, - { - name: "梨花白", - thumbnail: "https://xximg1.meitudata.com/bBLUvgJmoK.jpeg!thumb-w321-webp75", - material_id: "20076051633" - }, - { - name: "青柠", - thumbnail: "https://xximg1.meitudata.com/gn9SOPYgQ6.jpeg!thumb-w321-webp75", - material_id: "20076051733" - }, - { - name: "西柚", - thumbnail: "https://xximg1.meitudata.com/WXgfmKYZJv.jpeg!thumb-w321-webp75", - material_id: "20076051628" - }, - { - name: "恋人未满", - thumbnail: "https://xximg1.meitudata.com/3YVfO0zBXJ.jpeg!thumb-w321-webp75", - material_id: "20076051075" - }, - { - name: "春樱", - thumbnail: "https://xximg1.meitudata.com/k5DIOxgJpN.jpeg!thumb-w321-webp75", - material_id: "20076291634" - }, - { - name: "自然", - thumbnail: "https://xximg1.meitudata.com/nBqUoGPX5Q.jpeg!thumb-w321-webp75", - material_id: "20076051907" - }, - { - name: "Forest", - thumbnail: "https://xximg1.meitudata.com/V2jTkoYa96.jpeg!thumb-w321-webp75", - material_id: "20106191835" - }, - { - name: "高级灰Ⅱ", - thumbnail: "https://xximg1.meitudata.com/LEKSJO5x8V.jpeg!thumb-w321-webp75", - material_id: "20076051853" - }, - { - name: "晴空", - thumbnail: "https://xximg1.meitudata.com/LEKSJO5VaL.jpeg!thumb-w321-webp75", - material_id: "20076052027" - }, - { - name: "白兔糖", - thumbnail: "https://xximg1.meitudata.com/k5DIOjzV9d.jpeg!thumb-w321-webp75", - material_id: "2007605675" - }, - { - name: "测", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/zwdte8R2e5KMMmm.jpeg!thumb-w321-webp75", - material_id: "506000000" - }, - { - name: "素颜", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/DyOHJNKyxWGbpeg.jpeg!thumb-w321-webp75", - material_id: "20076052222" - }, - { - name: "奶杏", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/3YVf1DOlq5jVlLe.jpeg!thumb-w321-webp75", - material_id: "20076052221" - }, - { - name: "蜜桃", - thumbnail: "https://xximg1.meitudata.com/9QktygQgDn.jpeg!thumb-w321-webp75", - material_id: "20076291732" - }, - { - name: "青柠", - thumbnail: "https://xximg1.meitudata.com/edzuvqDqwJ.jpeg!thumb-w321-webp75", - material_id: "20076291733" - }, - { - name: "西柚", - thumbnail: "https://xximg1.meitudata.com/kGVuOxgxm5.jpeg!thumb-w321-webp75", - material_id: "20076291628" - }, - { - name: "牛油果", - thumbnail: "https://xximg1.meitudata.com/1zXU1zyp3V.jpeg!thumb-w321-webp75", - material_id: "20076292053" - }, - { - name: "胶片字体1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/wkQsv9BqOlk01j6.jpeg!thumb-w321-webp75", - material_id: "506001241" - }, - { - name: "503cw", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/YadfjNxP2ng3b0v.jpeg!thumb-w321-webp75", - material_id: "506001236" - }, - { - name: "爱心", - thumbnail: "https://xximg1.meitudata.com/8D5Fwm9VLE.jpeg!thumb-w321-webp75", - material_id: "20079971111" - }, - { - name: "", - thumbnail: "https://xximg1.meitudata.com/xkbsQOl9bX.jpeg!thumb-w321-webp75", - material_id: "20079971995" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/YadfjWvZDeK8Pjb.jpeg!thumb-w321-webp75", - material_id: "200799700078" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/54Kf8ZNP6YkBdnZ.jpeg!thumb-w321-webp75", - material_id: "200799700077" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/20dfEzWa33vvbEJ.jpeg!thumb-w321-webp75", - material_id: "200799700076" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/L3xFj5Y1l5qyO8k.jpeg!thumb-w321-webp75", - material_id: "200799700075" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/qp4IRaqOvg208Vx.jpeg!thumb-w321-webp75", - material_id: "200799700074" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/opkIW3BY6gJ6DP8.jpeg!thumb-w321-webp75", - material_id: "200799700073" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/39nT1zN9v2p5BBy.jpeg!thumb-w321-webp75", - material_id: "200799700072" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/Kz3fp1lekbGJY52.jpeg!thumb-w321-webp75", - material_id: "200799700071" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/20dfEz9V3L935WD.jpeg!thumb-w321-webp75", - material_id: "200799700070" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/R3GFJpZEd0pD5xY.jpeg!thumb-w321-webp75", - material_id: "200799700069" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/54Kf8o3GlPJgW3Q.jpeg!thumb-w321-webp75", - material_id: "200799700067" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/DaofJN9Gzxd8O5a.jpeg!thumb-w321-webp75", - material_id: "200799700063" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/qp4IRN9PaZ1Penl.jpeg!thumb-w321-webp75", - material_id: "200799700055" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/vkRsK6OxQ60gkJR.jpeg!thumb-w321-webp75", - material_id: "200799700045" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/1ZLIm5ExEmVpobj.jpeg!thumb-w321-webp75", - material_id: "200799700053" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/wkQsvL9y9XX1ZL8.jpeg!thumb-w321-webp75", - material_id: "200799700054" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/BmDFqnXBXod6LKx.jpeg!thumb-w321-webp75", - material_id: "200799700061" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/39nT1DmnGNQp8Gy.jpeg!thumb-w321-webp75", - material_id: "200799700060" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/k2ou2a6aPWDR5nX.jpeg!thumb-w321-webp75", - material_id: "200799700058" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/Q3bFyBOB1DxY6Dx.jpeg!thumb-w321-webp75", - material_id: "200799700059" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/O3RFRL3v0Jpd0Wk.jpeg!thumb-w321-webp75", - material_id: "200799700057" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/BmDFqnWadYGzXxP.jpeg!thumb-w321-webp75", - material_id: "20079970049" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/Q3bFyBQ8JLx2Wvb.jpeg!thumb-w321-webp75", - material_id: "20079970051" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/vkRsK6eLxeGj9w8.jpeg!thumb-w321-webp75", - material_id: "200799700041" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/gkmsKBGp36w8wEO.jpeg!thumb-w321-webp75", - material_id: "200799700056" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/ee9h6Qk4Y1wJpoY.jpeg!thumb-w321-webp75", - material_id: "200799700047" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/jeyhVvLBDqo64yy.jpeg!thumb-w321-webp75", - material_id: "20079970050" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/DaofJNY4pKnpQxa.jpeg!thumb-w321-webp75", - material_id: "20079970053" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/99ETG9z6NzjmBwQ.jpeg!thumb-w321-webp75", - material_id: "200799700046" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/ee9h6QD4PGmGBGa.jpeg!thumb-w321-webp75", - material_id: "200799700042" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/BmDFqnwGbGvXPq3.png!thumb-w321-webp75", - material_id: "200799700038" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/VwJigjRRPoRz8Xl.png!thumb-w321-webp75", - material_id: "200799700039" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/54Kf8BmB9dyqY2k.jpeg!thumb-w321-webp75", - material_id: "200799700027" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/ze1hXRywlnq18zj.jpeg!thumb-w321-webp75", - material_id: "200799700025" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/xkbs1eVvOEB46wE.jpeg!thumb-w321-webp75", - material_id: "200799700032" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/bxgsY85V4j5W3PY.jpeg!thumb-w321-webp75", - material_id: "200799700034" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/54Kf8oxaX5kVpN6.jpeg!thumb-w321-webp75", - material_id: "200799700033" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/EVvIj5VjGJyBNLO.png!thumb-w321-webp75", - material_id: "200799700031" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/Q3bFyJ2yqXlmJJz.png!thumb-w321-webp75", - material_id: "200799700030" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/1ZLImgemPooxWqv.jpeg!thumb-w321-webp75", - material_id: "200799700024" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8D5FJjdJmO4VRbg.jpeg!thumb-w321-webp75", - material_id: "200799700029" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/EVvIj595ekZpN3X.jpeg!thumb-w321-webp75", - material_id: "200799700023" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/vkRsKg3OONyzZLb.png!thumb-w321-webp75", - material_id: "200799700021" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/496TndEvWqJynwo.png!thumb-w321-webp75", - material_id: "200799700019" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8D5FJjkBmkNlpkd.png!thumb-w321-webp75", - material_id: "200799700020" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/WPWF3ODzRYPzo3K.jpeg!thumb-w321-webp75", - material_id: "200799700018" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/PVQIdBDb3b998XB.jpeg!thumb-w321-webp75", - material_id: "200799700017" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/99ETGwpo0Lv8dEZ.jpeg!thumb-w321-webp75", - material_id: "200799700008" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/gkmsKQKGPVm3j8E.jpeg!thumb-w321-webp75", - material_id: "200799700012" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8D5FJjJJ310zYBQ.jpeg!thumb-w321-webp75", - material_id: "200799700010" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/lpvIjojjEYRbwWL.jpeg!thumb-w321-webp75", - material_id: "200799700011" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/xkbs1e1ee83wlP0.jpeg!thumb-w321-webp75", - material_id: "200799700014" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/DaofJWJnxRe3W09.jpeg!thumb-w321-webp75", - material_id: "200799700015" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/XRLI50Bdlg9joZ2.png!thumb-w321-webp75", - material_id: "200799700009" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/99ETGwkej4aqVjo.png!thumb-w321-webp75", - material_id: "200799700016" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/VwJikyzE29.png!thumb-w321-webp75", - material_id: "200799700000" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/ze1h5O9oKb.jpeg!thumb-w321-webp75", - material_id: "200799700002" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/a3wFLXLnx2.jpeg!thumb-w321-webp75", - material_id: "200799700001" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/6DqFRdykvn.png!thumb-w321-webp75", - material_id: "200799700004" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/1ZLI1pjZgP.jpeg!thumb-w321-webp75", - material_id: "200799700003" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/opkIn0ZXgW.jpeg!thumb-w321-webp75", - material_id: "200799700006" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/ze1h5VwNLD.jpeg!thumb-w321-webp75", - material_id: "20079976666" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/mlahykPGYd.jpeg!thumb-w321-webp75", - material_id: "2007997666" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/XRLIPQX4qj.jpeg!thumb-w321-webp75", - material_id: "20079972" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/k2ouOWwP2b.jpeg!thumb-w321-webp75", - material_id: "20079971" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/xkbsQP2W5z.jpeg!thumb-w321-webp75", - material_id: "20079971234" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/EVvIjl5B2q1P3Ld.jpeg!thumb-w321-webp75", - material_id: "200799700062" - }, - { - name: "新年静物", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8D5FJzenN2v99KG.jpeg!thumb-w321-webp75", - material_id: "200799700079" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/20dfEzd1lndjOwg.jpeg!thumb-w321-webp75", - material_id: "20079972282" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/nplIyLnnEm9o41W.png!thumb-w321-webp75", - material_id: "200799700022" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/ze1h5ONoeg.png!thumb-w321-webp75", - material_id: "200799700007" - }, - { - name: "原生", - thumbnail: "https://xximg1.meitudata.com/XeVHPQpbgR.jpeg!thumb-w321-webp75", - material_id: "20079930000" - }, - { - name: "减龄2", - thumbnail: "https://xximg1.meitudata.com/GPoUPW5m8n.jpeg!thumb-w321-webp75", - material_id: "20079930003" - }, - { - name: "质感", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/nBJcyLgw0QZmvB2.png!thumb-w321-webp75", - material_id: "20079932147" - }, - { - name: "眼睑下至", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/1zVH1xKebW.jpeg!thumb-w321-webp75", - material_id: "20079930007" - }, - { - name: "原生2", - thumbnail: "https://xximg1.meitudata.com/aOWsLZ14vp.jpeg!thumb-w321-webp75", - material_id: "20079930001" - }, - { - name: "减龄2优化", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/5JOiDRnL9l.jpeg!thumb-w321-webp75", - material_id: "20079930070" - }, - { - name: "DIOR", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/5JOi8BVv09mRyGK.jpeg!thumb-w321-webp75", - material_id: "20079930075" - }, - { - name: "", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/N3OFqdNzda.jpeg!thumb-w321-webp75", - material_id: "20079932157" - }, - { - name: "bling", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/9nzUy0eQGq.png!thumb-w321-webp75", - material_id: "20079932158" - }, - { - name: "色散光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/JdaUWkbq92.png!thumb-w321-webp75", - material_id: "20079932154" - }, - { - name: "闪光夏日", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/bBLUvkadVZ.jpeg!thumb-w321-webp75", - material_id: "20079932153" - }, - { - name: "闪光女团", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/qR1tJPGR0j.jpeg!thumb-w321-webp75", - material_id: "20079930053" - }, - { - name: "闪光少女", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/eqOHv3wnlD.jpeg!thumb-w321-webp75", - material_id: "20079932152" - }, - { - name: "bling01", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/EPdULXNn66.jpeg!thumb-w321-webp75", - material_id: "20079930010" - }, - { - name: "bling02", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/6kOHR9OGvP.jpeg!thumb-w321-webp75", - material_id: "20079930011" - }, - { - name: "bling03", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/eqOHv38nPz.jpeg!thumb-w321-webp75", - material_id: "20079930012" - }, - { - name: "Disco6", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/RwXHmzP9Y0.jpeg!thumb-w321-webp75", - material_id: "20079930009" - }, - { - name: "Dream1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/XeVHPQWGGZ.jpeg!thumb-w321-webp75", - material_id: "20079930013" - }, - { - name: "Hypno1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/j4QUeORQE4.jpeg!thumb-w321-webp75", - material_id: "20079930014" - }, - { - name: "Stellar2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/5JOiDpKNRz.jpeg!thumb-w321-webp75", - material_id: "20079930008" - }, - { - name: "光1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/OX8i06ZB9D.jpeg!thumb-w321-webp75", - material_id: "20079930004" - }, - { - name: "Prisma1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/JdXHWkgq5D.jpeg!thumb-w321-webp75", - material_id: "20079930017" - }, - { - name: "Plastic1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/V2jTkWZbP6.jpeg!thumb-w321-webp75", - material_id: "20079930016" - }, - { - name: "莫奈", - thumbnail: "https://xximg1.meitudata.com/9nzUy0bwDV.png!thumb-w321-webp75", - material_id: "20079932086" - }, - { - name: "雾柔水光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/JdaUWkEplJ.png!thumb-w321-webp75", - material_id: "20079932146" - }, - { - name: "muji", - thumbnail: "https://xximg1.meitudata.com/LEasJNJplP.png!thumb-w321-webp75", - material_id: "20079932148" - }, - { - name: "相机打光测试", - thumbnail: "https://xximg1.meitudata.com/j4QUexO3K5.jpeg!thumb-w321-webp75", - material_id: "20079932054" - }, - { - name: "1222", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/3XwSOKRJjx.jpeg!thumb-w321-webp75", - material_id: "20079931222" - }, - { - name: "1333", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/EQYuL9LqvV.jpeg!thumb-w321-webp75", - material_id: "20079931333" - }, - { - name: "1999", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/VqVfkykO5y.jpeg!thumb-w321-webp75", - material_id: "20079931999" - }, - { - name: "1666", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/1pgI1plVVv.jpeg!thumb-w321-webp75", - material_id: "20079931666" - }, - { - name: "1888", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/OJbF06VvmK.jpeg!thumb-w321-webp75", - material_id: "20079931888" - }, - { - name: "1888", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/ke8UOWwVz6.jpeg!thumb-w321-webp75", - material_id: "20079931777" - }, - { - name: "2057", - thumbnail: "https://xximg1.meitudata.com/3YVfOVKVmm.jpeg!thumb-w321-webp75", - material_id: "20079932057" - }, - { - name: "五一鱼块", - thumbnail: "https://xximg1.meitudata.com/39nTOVegOn.jpeg!thumb-w321-webp75", - material_id: "20079932056" - }, - { - name: "五一", - thumbnail: "https://xximg1.meitudata.com/bBZcvykbxP.png!thumb-w321-webp75", - material_id: "20079932053" - }, - { - name: "bling04", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/OX8i06XB3L.jpeg!thumb-w321-webp75", - material_id: "20079930015" - }, - { - name: "元旦", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/mbZHO6bOgR5YgdB.jpeg!thumb-w321-webp75", - material_id: "20106401993" - }, - { - name: "瞬间", - thumbnail: "https://xximg1.meitudata.com/y59Ib9JKjm.jpeg!thumb-w321-webp75", - material_id: "20106401997" - }, - { - name: "新专辑轰顶", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/BR7fMkae0elL9DK.png!thumb-w321-webp75", - material_id: "506093294" - }, - { - name: "新素材", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/LL8SozrWaMVRP5L.png!thumb-w321-webp75", - material_id: "506093299" - }, - { - name: "素材不在功能页", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/69MiA9JxOErJvvP.png!thumb-w321-webp75", - material_id: "506093238" - }, - { - name: "我被禁用了😄我被禁用了😄", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/D18Iz3ZaAPm0za3.png!thumb-w321-webp75", - material_id: "506093239" - }, - { - name: "低版本不可见", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/757SWMrAkzKAorX.png!thumb-w321-webp75", - material_id: "506093243" - }, - { - name: "3d立体弹窗", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/k4gtA430gyk3Wx9.png!thumb-w321-webp75", - material_id: "506093241" - }, - { - name: "加州", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/RKgUJnKn126zbGG.jpeg!thumb-w321-webp75", - material_id: "50602339" - }, - { - name: "爱心光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/j4GcVXWgv200k0D.jpeg!thumb-w321-webp75", - material_id: "506002288" - }, - { - name: "2053太空漫游", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/wgNUven5XxgKmG4.jpeg!thumb-w321-webp75", - material_id: "20096492276" - }, - { - name: "CCD", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/XepU5b3WG3BDJNp.jpeg!thumb-w321-webp75", - material_id: "20096492265" - }, - { - name: "IMAX胶片", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/bBZcYlQVDNVde5Y.jpeg!thumb-w321-webp75", - material_id: "20096492254" - }, - { - name: "皮囊之下", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/p0LTO938OKJLV2k.jpeg!thumb-w321-webp75", - material_id: "506002350" - }, - { - name: "号手就位", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/J34FBw2bEwzGbWp.jpeg!thumb-w321-webp75", - material_id: "506002317" - }, - { - name: "海绵3", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/26guLmK1p0M13Zr.jpeg!thumb-w321-webp75", - material_id: "506010277" - }, - { - name: "海绵4", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/KgaiJr4o77waN4d.jpeg!thumb-w321-webp75", - material_id: "506010276" - }, - { - name: "海绵1", - thumbnail: "https://xxtool-release.zone1.meitudata.com/xxtool-pre/material/71MfrYOoEDyKLJA.jpeg!thumb-w321-webp75", - material_id: "506010279" - }, - { - name: "TR2", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/k51F2wNOkqQgBjX.jpeg!thumb-w321-webp75", - material_id: "506002345" - }, - { - name: "TR3", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/JdaUB94WJJqQlwK.jpeg!thumb-w321-webp75", - material_id: "506002344" - }, - { - name: "TR4", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/5JeH8WzDLm1NmPy.jpeg!thumb-w321-webp75", - material_id: "506002347" - }, - { - name: "TR5", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/lkjUjGvk4VORqlR.jpeg!thumb-w321-webp75", - material_id: "506002343" - }, - { - name: "TR6", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/k51F2wNOLjj94dK.jpeg!thumb-w321-webp75", - material_id: "506002346" - }, - { - name: "乌布", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/V2xig11N0vQLpxm.jpeg!thumb-w321-webp75", - material_id: "506002333" - }, - { - name: "微缩世界", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/oJQiWgqo5kodE1B.jpeg!thumb-w321-webp75", - material_id: "506002298" - }, - { - name: "微缩世界Ⅱ", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/N8RHzP6aP44Xex6.jpeg!thumb-w321-webp75", - material_id: "506002299" - }, - { - name: "旅程", - thumbnail: "https://xximg1.meitudata.com/V2jTkEY8Y9.jpeg!thumb-w321-webp75", - material_id: "20086101254" - }, - { - name: "旅程", - thumbnail: "https://xximg1.meitudata.com/GPoUPwOj1b.jpeg!thumb-w321-webp75", - material_id: "20086101315" - }, - { - name: "且听风吟", - thumbnail: "https://xximg1.meitudata.com/Z3VspeGOy3.jpeg!thumb-w321-webp75", - material_id: "2008610635" - }, - { - name: "Sky", - thumbnail: "https://xximg1.meitudata.com/p0GcZPbnV0.jpeg!thumb-w321-webp75", - material_id: "20086101612" - }, - { - name: "清新蓝", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/BbWiqXBp8X869e9.jpeg!thumb-w321-webp75", - material_id: "20086162162" - }, - { - name: "男友力", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/6PocqE9aB8nZgOl.jpeg!thumb-w321-webp75", - material_id: "20086161259" - }, - { - name: "男友力", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8boiJnm9e69J3GB.jpeg!thumb-w321-webp75", - material_id: "20086161256" - }, - { - name: "男友力", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/oeGiWgmVL06ppzo.jpeg!thumb-w321-webp75", - material_id: "20086161469" - }, - { - name: "chiffon", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/JdXHB6PXY9dzmjl.jpeg!thumb-w321-webp75", - material_id: "2008607975" - }, - { - name: "新年限定", - thumbnail: "https://xximg1.meitudata.com/mlahymLEBk.jpeg!thumb-w321-webp75", - material_id: "20086072015" - }, - { - name: "新春食堂", - thumbnail: "https://xximg1.meitudata.com/dDpFaD19J9.jpeg!thumb-w321-webp75", - material_id: "20086071687" - }, - { - name: "围炉", - thumbnail: "https://xximg1.meitudata.com/jo2UegGjqj.jpeg!thumb-w321-webp75", - material_id: "20086071688" - }, - { - name: "橘子汽水", - thumbnail: "https://xximg1.meitudata.com/voWUlvO8Dd.jpeg!thumb-w321-webp75", - material_id: "2008607571" - }, - { - name: "舒芙蕾", - thumbnail: "https://xximg1.meitudata.com/xmdFQwl5J0.jpeg!thumb-w321-webp75", - material_id: "2008607569" - }, - { - name: "野Ⅰ", - thumbnail: "https://xximg1.meitudata.com/6KwCRlbG6g.jpeg!thumb-w321-webp75", - material_id: "20086070001" - }, - { - name: "野餐Ⅱ", - thumbnail: "https://xximg1.meitudata.com/Nzjuq2XDGQ.jpeg!thumb-w321-webp75", - material_id: "20086070002" - }, - { - name: "食欲", - thumbnail: "https://xximg1.meitudata.com/jo2UegGzVl.jpeg!thumb-w321-webp75", - material_id: "20086071585" - }, - { - name: "食欲", - thumbnail: "https://xximg1.meitudata.com/0J6fWJbYW8.jpeg!thumb-w321-webp75", - material_id: "20086071587" - }, - { - name: "橘子汽水", - thumbnail: "https://xximg1.meitudata.com/bz0uvoO5Po.jpeg!thumb-w321-webp75", - material_id: "20086071908" - }, - { name: "", thumbnail: "https://xximg1.meitudata.com/xmdFQwgNGx.png!thumb-w321-webp75", material_id: "2008607014" }, - { - name: "粉花面包", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/gkmsKBakKNO6ynd.jpeg!thumb-w321-webp75", - material_id: "2008607973" - }, - { name: "", thumbnail: "https://xximg1.meitudata.com/RzLumonEvG.png!thumb-w321-webp75", material_id: "2008607015" }, - { - name: "Brunch", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/V2jTgxyR4xG9k4d.jpeg!thumb-w321-webp75", - material_id: "20086072197" - }, - { - name: "vista200", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/LJwUjvN4awZDWOw.jpeg!thumb-w321-webp75", - material_id: "506002301" - }, - { - name: "胶片时光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/kBwi28Wm6y698oy.jpeg!thumb-w321-webp75", - material_id: "20086171381" - }, - { - name: "胶片时光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/8boiJnmYZ8NbvJ6.jpeg!thumb-w321-webp75", - material_id: "20086171339" - }, - { - name: "胶片时光", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/vEdtKOx0n2gEyBg.jpeg!thumb-w321-webp75", - material_id: "20086171336" - }, - { - name: "菲林日记", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/OdpiRJVedvxlR4m.jpeg!thumb-w321-webp75", - material_id: "20096221571" - }, - { - name: "菲林日记", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/kBwi28w05RB11de.jpeg!thumb-w321-webp75", - material_id: "20096221570" - }, - { - name: "菲林日记", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/KBPipZwDElPR1bl.jpeg!thumb-w321-webp75", - material_id: "20096221783" - }, - { - name: "菲林日记", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/2bkiEXqkWmBZzjL.jpeg!thumb-w321-webp75", - material_id: "20096221781" - }, - { - name: "菲林日记", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/PnkTd0zBvKKRYb0.jpeg!thumb-w321-webp75", - material_id: "20096221780" - }, - { - name: "Outdoors", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/NL9HzPdl9WLnlq9.jpeg!thumb-w321-webp75", - material_id: "20086172023" - }, - { - name: "蒸汽波", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/DyjUJpgEgyP8nDL.jpeg!thumb-w321-webp75", - material_id: "506002182" - }, - { - name: "落日", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/V2jTgj1akYWmEvP.jpeg!thumb-w321-webp75", - material_id: "20106112264" - }, - { - name: "音浪", - thumbnail: "http://xximg2.meitudata.com/VXgfk8zEJ3.jpeg!thumb-w321-webp75", - material_id: "20106111100" - }, - { - name: "毕业季", - thumbnail: "https://xxtool-release.zone1.meitudata.com/material/y56Fb5qaXq.jpeg!thumb-w321-webp75", - material_id: "20076121254" - }, - { - name: "游乐园", - thumbnail: "https://xximg1.meitudata.com/N8eUq2XE0E.jpeg!thumb-w321-webp75", - material_id: "20076091595" - }, - { - name: "粉黛", - thumbnail: "https://xximg1.meitudata.com/wgNU3GlNRj.jpeg!thumb-w321-webp75", - material_id: "20076091618" - }, - { - name: "游乐园", - thumbnail: "https://xximg1.meitudata.com/Z3GfpjWOKV.jpeg!thumb-w321-webp75", - material_id: "20076091596" - }, - { - name: "水彩画", - thumbnail: "https://xximg1.meitudata.com/eqOHvqo0ej.jpeg!thumb-w321-webp75", - material_id: "20106041764" - }, - { - name: "钢笔淡彩", - thumbnail: "https://xximg1.meitudata.com/V2jTkopPGQ.jpeg!thumb-w321-webp75", - material_id: "20106041765" - }, - { - name: "质感", - thumbnail: "https://xximg1.meitudata.com/XepUPNP4WP.png!thumb-w321-webp75", - material_id: "20076061608" - }, - { - name: "质感", - thumbnail: "https://xximg1.meitudata.com/9nzUydyWeB.png!thumb-w321-webp75", - material_id: "20076061589" - }, - { - name: "质感", - thumbnail: "http://xximg2.meitudata.com/yObUbokjlb.jpeg!thumb-w321-webp75", - material_id: "20076061609" - }, - { - name: "荼白", - thumbnail: "https://xximg1.meitudata.com/6kOHRW8WGD.jpeg!thumb-w321-webp75", - material_id: "20076061859" - } - ] - - public totalCount(): number { - return this.dataArray.length; - } - - public getData(index: number): Material | undefined { - return this.dataArray[index]; - } - - public addData(index: number, data: Material): void { - this.dataArray.splice(index, 0, data); - this.notifyDataAdd(index); - } - - public pushData(data: Material): void { - this.dataArray.push(data); - this.notifyDataAdd(this.dataArray.length - 1); - } -} diff --git a/entry/src/main/ets/pages/ObjectFitPage.ets b/entry/src/main/ets/pages/ObjectFitPage.ets index c86415f..58ca101 100644 --- a/entry/src/main/ets/pages/ObjectFitPage.ets +++ b/entry/src/main/ets/pages/ObjectFitPage.ets @@ -15,49 +15,49 @@ import { ImageKnife, ImageKnifeComponent, ImageKnifeOption } from '@ohos/libraryimageknife' @Entry -@Component +@ComponentV2 struct ObjectFitPage { - @State imageKnifeOption: ImageKnifeOption = { + @Local imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r("app.media.app_icon"), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Fill - } + }) build() { Column() { - Button("主图Fill拉伸填充").onClick(()=>{ - this.imageKnifeOption = { + Button($r('app.string.Main_image_Fill')).onClick(()=>{ + this.imageKnifeOption = new ImageKnifeOption({ loadSrc: $r("app.media.app_icon"), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Fill - } + }) }) - Button("占位图Contain保持比例填充").margin({top:10}).onClick(async () => { + Button($r('app.string.Maintain_proportion_filling')).margin({top:10}).onClick(async () => { ImageKnife.getInstance().removeAllMemoryCache() await ImageKnife.getInstance().removeAllFileCache() - this.imageKnifeOption = { + this.imageKnifeOption = new ImageKnifeOption({ loadSrc: "https://wx2.sinaimg.cn/mw690/006HyQKGgy1hnqp08dw09j30u04twu0x.jpg", placeholderSrc: $r("app.media.app_icon"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Fill, placeholderObjectFit: ImageFit.Contain - } + }) }) - Button("错误图None不变化").margin({top:10}).onClick(() => { - this.imageKnifeOption = { + Button($r('app.string.Error_graph_None')).margin({top:10}).onClick(() => { + this.imageKnifeOption = new ImageKnifeOption({ loadSrc: "http://xxxxx", placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Fill, errorholderObjectFit: ImageFit.None - } + }) }) ImageKnifeComponent({ diff --git a/entry/src/main/ets/pages/SignatureTestPage.ets b/entry/src/main/ets/pages/SignatureTestPage.ets index 42f49e4..8fc1763 100644 --- a/entry/src/main/ets/pages/SignatureTestPage.ets +++ b/entry/src/main/ets/pages/SignatureTestPage.ets @@ -16,45 +16,45 @@ import { ImageKnifeComponent, ImageKnifeOption } from '@ohos/libraryimageknife'; @Entry -@Component +@ComponentV2 struct SignatureTestPage { - @State imageKnifeOption1: ImageKnifeOption = + @Local imageKnifeOption1: ImageKnifeOption =new ImageKnifeOption( { loadSrc: $r('app.media.icon'), placeholderSrc:$r("app.media.loading"), - }; - @State imageKnifeOption2: ImageKnifeOption = + }); + @Local imageKnifeOption2: ImageKnifeOption =new ImageKnifeOption( { loadSrc: $r('app.media.icon'), placeholderSrc:$r("app.media.loading"), - }; + }); build() { Scroll() { Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { - Text("key固定为 1").fontSize(15) + Text($r('app.string.The_key_fixed_1')).fontSize(15) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { - Button("加载") + Button($r('app.string.Load')) .onClick(() => { - this.imageKnifeOption1 = { + this.imageKnifeOption1 = new ImageKnifeOption({ loadSrc: 'https://img-blog.csdn.net/20140514114029140', placeholderSrc:$r("app.media.loading"), signature: "1" - } + }) }).margin({ top: 5, left: 3 }) ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).width(300).height(300) }.width('100%').backgroundColor(Color.Pink) - Text("key每次变化:时间戳").fontSize(15) + Text($r('app.string.The_key_changes_timestamp')).fontSize(15) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { - Button("加载") + Button($r('app.string.Load')) .onClick(() => { - this.imageKnifeOption2 = { + this.imageKnifeOption2 = new ImageKnifeOption({ loadSrc: 'https://img-blog.csdn.net/20140514114029140', placeholderSrc:$r("app.media.loading"), signature: new Date().getTime().toString() - } + }) }).margin({ top: 5, left: 3 }) ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption2 }).width(300).height(300) }.width('100%').backgroundColor(Color.Pink) diff --git a/entry/src/main/ets/pages/SingleImage.ets b/entry/src/main/ets/pages/SingleImage.ets index b7fc9b3..3671c6c 100644 --- a/entry/src/main/ets/pages/SingleImage.ets +++ b/entry/src/main/ets/pages/SingleImage.ets @@ -12,19 +12,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeComponent,BlurTransformation } from '@ohos/libraryimageknife'; +import { ImageKnifeComponent,BlurTransformation,ImageKnifeOption } from '@ohos/libraryimageknife'; import fs from '@ohos.file.fs'; import image from '@ohos.multimedia.image'; import { common2D, drawing } from '@kit.ArkGraphics2D'; @Entry -@Component +@ComponentV2 struct SingleImage { resource: string = "app.media.svgSample" scroller: Scroller = new Scroller; localFile: string = getContext(this).filesDir + "/icon.png" - @State pixelMap:PixelMap | undefined = undefined; - @State DrawingColorFilter: ColorFilter | undefined = undefined + @Local pixelMap:PixelMap | undefined = undefined; + @Local DrawingColorFilter: ColorFilter | undefined = undefined private color: common2D.Color = { alpha: 255, red: 255, green: 0, blue: 0 }; aboutToAppear(): void { // 拷贝本地文件 @@ -42,66 +42,66 @@ struct SingleImage { build() { Scroll(this.scroller) { Column() { - Text("本地资源svg图片") + Text($r('app.string.Local_SVG')) .fontSize(30) .fontWeight(FontWeight.Bold) ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc: $r("app.media.svgSample"), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.failed"), objectFit: ImageFit.Contain - } + }) }).width(100).height(100) .onClick(()=>{ this.DrawingColorFilter = drawing.ColorFilter.createBlendModeColorFilter(this.color, drawing.BlendMode.SRC_IN); }) - Text("本地context files下文件") + Text($r('app.string.Under_context_file')) .fontSize(30) .fontWeight(FontWeight.Bold) ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc: this.localFile, placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.failed"), objectFit: ImageFit.Contain - } + }) }).width(100).height(100) - Text("网络图片") + Text($r('app.string.Network_images')) .fontSize(30) .fontWeight(FontWeight.Bold) ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.failed"), objectFit: ImageFit.Contain, progressListener:(progress:number)=>{console.info("ImageKnife:: call back progress = " + progress)} - } + }) }).width(100).height(100) - Text("自定义下载") + Text($r('app.string.Custom_network_download')) .fontSize(30) .fontWeight(FontWeight.Bold) ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc: "https://file.atomgit.com/uploads/user/1704857786989_8994.jpeg", placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.failed"), objectFit: ImageFit.Contain, customGetImage: custom, transformation: new BlurTransformation(10) - } + }) }).width(100).height(100) - Text("pixelMap加载图片") + Text($r('app.string.PixelMap_loads_images')) .fontSize(30) .fontWeight(FontWeight.Bold) ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc: this.pixelMap!, placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.failed"), objectFit: ImageFit.Contain, - } + }) }).width(100).height(100) } .width('100%') diff --git a/entry/src/main/ets/pages/TestCacheDataPage.ets b/entry/src/main/ets/pages/TestCacheDataPage.ets new file mode 100644 index 0000000..e8da530 --- /dev/null +++ b/entry/src/main/ets/pages/TestCacheDataPage.ets @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ImageKnife, CacheStrategy, ImageKnifeComponent, ImageKnifeOption } from '@ohos/imageknife'; + +@Entry +@ComponentV2 +struct TestCacheDataPage { + @Local cacheUpLimit: number = 0; + @Local currentNum: number = 0; + @Local currentSize: number = 0; + @Local currentWidth: number = 200 + @Local currentHeight: number = 200 + @Local markersLimitText: string = getContext(this).resourceManager.getStringSync($r('app.string.memory')) + @Local markersNumText: string = getContext(this).resourceManager.getStringSync($r('app.string.memory')) + @Local markersSizeText: string = getContext(this).resourceManager.getStringSync($r('app.string.memory')) + @Local ImageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ + loadSrc: "", + objectFit: ImageFit.Contain, + onLoadListener: { + onLoadFailed: (err) => { + console.error("Load Failed Reason: " + err); + }, + onLoadSuccess: (data) => { + return data; + }, + }, + border: { radius: 50 } + }) + + aboutToAppear(): void { + ImageKnife.getInstance().initFileCache(getContext(this), 256, 256 * 1024 * 1024, "ImageKnifeCache1") + } + + build() { + Column() { + + ImageKnifeComponent( + { imageKnifeOption: this.ImageKnifeOption }) + .height(this.currentHeight) + .width(this.currentWidth) + .margin({ top: 10 }) + + Button($r('app.string.load_memory')) + .onClick(() => { + this.ImageKnifeOption = new ImageKnifeOption({ + loadSrc: "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/3e/v3/LqRoLI-PRSu9Nqa8KdJ-pQ/dSqskBpSR9eraAMn7NBdqA.jpg", + objectFit: ImageFit.Contain, + writeCacheStrategy: CacheStrategy.Memory, + border: { radius: 50 }, + }) + }) + Button($r('app.string.load_disk')) + .onClick(() => { + this.ImageKnifeOption = new ImageKnifeOption({ + loadSrc: "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/56/v3/8MdhfSsCSMKj4sA6okUWrg/5uBx56tLTUO3RYQl-E5JiQ.jpg", + objectFit: ImageFit.Contain, + writeCacheStrategy: CacheStrategy.File, + border: { radius: 50 }, + }) + }) + Text($r('app.string.cur_cache_limit', this.markersLimitText, this.cacheUpLimit)) + .fontSize(20) + .margin({ bottom: 8 }); + Text($r('app.string.cur_cache_image_num', this.markersNumText, this.currentNum)) + .fontSize(20) + .margin({ bottom: 8 }); + Text($r('app.string.cur_cache_size', this.markersSizeText, this.currentSize)).fontSize(20).margin({ bottom: 20 }); + + Button($r('app.string.get_cur_memory_limit')).onClick(() => { + let result = ImageKnife.getInstance().getCacheUpperLimit(CacheStrategy.Memory); + this.markersLimitText = getContext(this).resourceManager.getStringSync($r('app.string.memory')) + if (result) { + this.cacheUpLimit = result / (1024 * 1024); + } else { + this.cacheUpLimit = 0; + } + }).margin({ bottom: 8 }); + Button($r('app.string.get_img_number_of_cache')).onClick(() => { + let result = ImageKnife.getInstance().getCurrentPicturesNum(CacheStrategy.Memory); + this.markersNumText = getContext(this).resourceManager.getStringSync($r('app.string.memory')) + if (result) { + this.currentNum = result; + } else { + this.currentNum = 0; + } + }).margin({ bottom: 8 }); + Button($r('app.string.get_cur_memory_size')).onClick(() => { + let result = ImageKnife.getInstance().getCurrentCacheSize(CacheStrategy.Memory); + this.markersSizeText = getContext(this).resourceManager.getStringSync($r('app.string.memory')) + if (result) { + this.currentSize = result / (1024 * 1024); + } else { + this.currentSize = 0; + } + }).margin({ bottom: 8 }); + + Button($r('app.string.get_cur_disk_limit')).onClick(() => { + let result = ImageKnife.getInstance().getCacheUpperLimit(CacheStrategy.File); + this.markersLimitText = getContext(this).resourceManager.getStringSync($r('app.string.disk')) + if (result) { + this.cacheUpLimit = result / (1024 * 1024); + } else { + this.cacheUpLimit = 0; + } + }).margin({ bottom: 8 }); + Button($r('app.string.get_img_number_of_disk')).onClick(() => { + let result = ImageKnife.getInstance().getCurrentPicturesNum(CacheStrategy.File); + this.markersNumText = getContext(this).resourceManager.getStringSync($r('app.string.disk')) + if (result) { + this.currentNum = result; + } else { + this.currentNum = 0; + } + }).margin({ bottom: 8 }); + Button($r('app.string.get_cur_disk_size')).onClick(() => { + let result = ImageKnife.getInstance().getCurrentCacheSize(CacheStrategy.File); + this.markersSizeText = getContext(this).resourceManager.getStringSync($r('app.string.disk')) + if (result) { + this.currentSize = result / (1024 * 1024); + } else { + this.currentSize = 0; + } + }).margin({ bottom: 8 }); + } + .height('100%').width('100%').margin({ top: 50 }) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/TestChangeColorPage.ets b/entry/src/main/ets/pages/TestChangeColorPage.ets new file mode 100644 index 0000000..2a282ae --- /dev/null +++ b/entry/src/main/ets/pages/TestChangeColorPage.ets @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { drawing, common2D } from '@kit.ArkGraphics2D'; +import { ImageKnifeComponent, ImageKnifeOption } from '@ohos/imageknife'; + +@Entry +@Component +struct TestChangeColorPage { + private imageOne: Resource = $r('app.media.ic_test_change_color_png'); + private imageTwo: Resource = $r('app.media.ic_test_change_color_png'); + @State src: Resource = this.imageOne + @State src2: Resource = this.imageTwo + @State color: common2D.Color = { + alpha: 255, + red: 255, + green: 1, + blue: 1 + }; + @State DrawingColorFilterFirst: ColorFilter | undefined = undefined + + build() { + Column() { + Text($r('app.string.select_color_btn')).margin({ top: 20 }) + Row() { + Button($r('app.string.red')).backgroundColor(Color.Red).margin(5).onClick(() => { + this.color = { + alpha: 255, + red: 255, + green: 1, + blue: 1 + }; + }) + Button($r('app.string.yellow')).backgroundColor(Color.Yellow).margin(5).onClick(() => { + this.color = { + alpha: 255, + red: 255, + green: 255, + blue: 1 + }; + }) + Button($r('app.string.green')).backgroundColor(Color.Green).margin(5).onClick(() => { + this.color = { + alpha: 255, + red: 1, + green: 255, + blue: 1 + }; + }) + Button($r('app.string.blue')).backgroundColor(Color.Blue).margin(5).onClick(() => { + this.color = { + alpha: 255, + red: 1, + green: 1, + blue: 255 + }; + }) + } + .width('100%') + .height(50) + .justifyContent(FlexAlign.Center) + + Text($r('app.string.master_image')).margin({ top: 20 }) + ImageKnifeComponent({ + imageKnifeOption: new ImageKnifeOption({ + loadSrc: this.src + }) + }).width(110).height(110) + + Text($r('app.string.click_img_to_change_color')).margin({ top: 30 }) + ImageKnifeComponent({ + imageKnifeOption: new ImageKnifeOption({ + loadSrc: this.src, + drawingColorFilter: this.DrawingColorFilterFirst + }) + }) + .onClick(() => { + this.DrawingColorFilterFirst = + drawing.ColorFilter.createBlendModeColorFilter(this.color, drawing.BlendMode.SRC_IN); + }).width(110).height(110) + + Text($r('app.string.test_non_svg_color')).margin({ top: 30 }) + ImageKnifeComponent({ + imageKnifeOption: new ImageKnifeOption({ + loadSrc: $r('app.media.ic_test_change_color_png'), + drawingColorFilter: drawing.ColorFilter.createBlendModeColorFilter(this.color, drawing.BlendMode.SRC_IN) + }) + }).width(110).height(110) + + Text($r('app.string.test_svg_color')).margin({ top: 30 }) + ImageKnifeComponent({ + imageKnifeOption: new ImageKnifeOption({ + loadSrc: $r("app.media.ic_test_change_color_svg"), + drawingColorFilter: drawing.ColorFilter.createBlendModeColorFilter(this.color, drawing.BlendMode.SRC_IN) + }) + }).width(110).height(110) + + }.width('100%').height('100%') + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/TestCommonImage.ets b/entry/src/main/ets/pages/TestCommonImage.ets index 669220c..66f3514 100644 --- a/entry/src/main/ets/pages/TestCommonImage.ets +++ b/entry/src/main/ets/pages/TestCommonImage.ets @@ -12,10 +12,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeComponent } from '@ohos/libraryimageknife'; +import { ImageKnifeComponent ,ImageKnifeOption} from '@ohos/libraryimageknife'; @Entry -@Component +@ComponentV2 struct TestCommonImage { private data: Array = [] aboutToAppear(): void { @@ -30,13 +30,13 @@ struct TestCommonImage { FlowItem() { Column(){ ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc: item, placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.failed"), objectFit: ImageFit.Contain, signature: "aaa" - } + }) }).width("50%").height(200) } }.height(200) diff --git a/entry/src/main/ets/pages/TestErrorHolderPage.ets b/entry/src/main/ets/pages/TestErrorHolderPage.ets index f49ab6c..0b716e2 100644 --- a/entry/src/main/ets/pages/TestErrorHolderPage.ets +++ b/entry/src/main/ets/pages/TestErrorHolderPage.ets @@ -12,34 +12,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeComponent } from '@ohos/libraryimageknife' +import { ImageKnifeComponent,ImageKnifeOption } from '@ohos/libraryimageknife' @Entry -@Component +@ComponentV2 struct TestErrorHolderPage { build() { Column() { Text("ImageKnifeComponent1").fontSize(20) ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc: "abc", errorholderSrc:$r('app.media.failed') - } + }) }).width(200).height(200) Text("ImageKnifeComponent2").fontSize(20) ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc: "abc", errorholderSrc:$r('app.media.startIcon') - } + }) }).width(200).height(200) Text("ImageKnifeComponent2").fontSize(20) ImageKnifeComponent({ - imageKnifeOption: { + imageKnifeOption: new ImageKnifeOption({ loadSrc: "abc", errorholderSrc:$r('app.media.mask_starfish') - } + }) }).width(200).height(200) } .height('100%') .width('100%') diff --git a/entry/src/main/ets/pages/TestHeader.ets b/entry/src/main/ets/pages/TestHeader.ets index 9be198e..50027b0 100644 --- a/entry/src/main/ets/pages/TestHeader.ets +++ b/entry/src/main/ets/pages/TestHeader.ets @@ -15,9 +15,9 @@ import { ImageKnifeComponent,ImageKnifeOption } from '@ohos/libraryimageknife' @Entry -@Component +@ComponentV2 struct TestPrefetchToFileCachePage { - @State imageKnifeOption: ImageKnifeOption = { + @Local imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", placeholderSrc:$r('app.media.loading'), headerOption:[ @@ -26,7 +26,7 @@ struct TestPrefetchToFileCachePage { value:"单个" } ] - } + }) build() { Column() { diff --git a/entry/src/main/ets/pages/TestHspPreLoadImage.ets b/entry/src/main/ets/pages/TestHspPreLoadImage.ets index 16ff464..e1084b8 100644 --- a/entry/src/main/ets/pages/TestHspPreLoadImage.ets +++ b/entry/src/main/ets/pages/TestHspPreLoadImage.ets @@ -15,7 +15,7 @@ import { IndexComponent } from "@ohos/libraryimageknife" @Entry -@Component +@ComponentV2 struct TestHspPreLoadImage { build() { Column() { diff --git a/entry/src/main/ets/pages/TestImageFlash.ets b/entry/src/main/ets/pages/TestImageFlash.ets index 75144e0..df2d363 100644 --- a/entry/src/main/ets/pages/TestImageFlash.ets +++ b/entry/src/main/ets/pages/TestImageFlash.ets @@ -12,9 +12,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeComponent } from '@ohos/libraryimageknife' +import { ImageKnifeComponent,ImageKnifeOption } from '@ohos/libraryimageknife' -@Observed +@ObservedV2 export class MsgModel { id: string cId: string @@ -30,10 +30,10 @@ export class MsgModel { } -@Reusable -@Component +// @Reusable +@ComponentV2 export struct MsgItem { - count: number = 0 + @Param count: number = 0 private data: Array = [ "http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg", "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg", @@ -59,37 +59,37 @@ export struct MsgItem { build(){ if (this.count % 2 == 0 && this.count <6){ ImageKnifeComponent({ - imageKnifeOption:{ + imageKnifeOption:new ImageKnifeOption({ loadSrc:$r("app.media.startIcon"), placeholderSrc:$r("app.media.loading") - },syncLoad:true + }),syncLoad:true }) }else if (this.count > 6 && this.count - 6 < this.data.length){ ImageKnifeComponent({ - imageKnifeOption:{ + imageKnifeOption:new ImageKnifeOption({ loadSrc:this.data[this.count - 6], placeholderSrc:$r("app.media.loading") - },syncLoad:true + }),syncLoad:true }) }else { ImageKnifeComponent({ - imageKnifeOption:{ + imageKnifeOption:new ImageKnifeOption({ loadSrc:$r("app.media.pngSample"), placeholderSrc:$r("app.media.loading") - },syncLoad:true + }),syncLoad:true }) } } } @Entry -@Component +@ComponentV2 struct ImageTestPage { count : number = 0 rCount: number = 0 scroller: Scroller = new Scroller() - @State list: MsgModel[] = [] - @State imageSize: number =100 + @Local list: MsgModel[] = [] + @Local imageSize: number =100 handAdd(){ this.count++ const msgItem = new MsgModel('add_id'+this.count, 'addBody'+this.count,'cId'+ this.count) @@ -111,12 +111,12 @@ struct ImageTestPage { }) } Row(){ - Text("点击尺寸加50") + Text($r('app.string.Click_on_add')) .onClick(()=> { this.imageSize = this.imageSize + 50 }) .width('50%').backgroundColor(0x88ff0000).textAlign(TextAlign.Center).height(50) - Text("点击尺寸减50") + Text($r('app.string.Click_on_reduce')) .onClick(()=> { this.imageSize = Math.max(this.imageSize - 50, 0) }) diff --git a/entry/src/main/ets/pages/TestIsUrlExist.ets b/entry/src/main/ets/pages/TestIsUrlExist.ets index 1225edc..da91f54 100644 --- a/entry/src/main/ets/pages/TestIsUrlExist.ets +++ b/entry/src/main/ets/pages/TestIsUrlExist.ets @@ -15,24 +15,24 @@ import { ImageKnifeComponent, ImageKnife, ImageKnifeOption, CacheStrategy } from '@ohos/libraryimageknife' @Entry -@Component +@ComponentV2 struct TestIsUrlExist { - @State imageKnifeOption: ImageKnifeOption = { + @Local imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r('app.media.startIcon'), placeholderSrc: $r('app.media.loading'), errorholderSrc:$r('app.media.failed') - } - @State source: PixelMap | string | Resource = $r("app.media.startIcon") - @State source1: PixelMap | string | Resource = $r("app.media.startIcon") + }) + @Local source: PixelMap | string | Resource = $r("app.media.startIcon") + @Local source1: PixelMap | string | Resource = $r("app.media.startIcon") build() { Column() { Flex() { - Button("预加载gif图").onClick(() => { + Button($r('app.string.Preloading_GIF')).onClick(() => { this.imageKnifeOption.loadSrc = "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658" }) - Button("内存缓存获取gif").onClick(() => { + Button($r('app.string.Retrieve_GIF_from_memory')).onClick(() => { ImageKnife.getInstance() .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", CacheStrategy.Memory) @@ -40,7 +40,7 @@ struct TestIsUrlExist { this.source = data !== undefined ? data.source : $r("app.media.startIcon") }) }) - Button("文件缓存获取gif").onClick(() => { + Button($r('app.string.Retrieve_GIF_from_disk')).onClick(() => { ImageKnife.getInstance() .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", CacheStrategy.File) @@ -51,11 +51,11 @@ struct TestIsUrlExist { } Flex() { - Button("预加载静态图").onClick(() => { + Button($r('app.string.Preloading_static_images')).onClick(() => { this.imageKnifeOption.loadSrc = 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp' }) - Button("内存缓存获取").onClick(() => { + Button($r('app.string.Retrieve_images_from_memory')).onClick(() => { ImageKnife.getInstance() .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', CacheStrategy.Memory) @@ -63,7 +63,7 @@ struct TestIsUrlExist { this.source = data!.source }) }) - Button("文件缓存获取").onClick(() => { + Button($r('app.string.Retrieve_images_from_disk')).onClick(() => { ImageKnife.getInstance() .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', CacheStrategy.File) diff --git a/entry/src/main/ets/pages/TestLoadCancelListenerPage.ets b/entry/src/main/ets/pages/TestLoadCancelListenerPage.ets new file mode 100644 index 0000000..5e97810 --- /dev/null +++ b/entry/src/main/ets/pages/TestLoadCancelListenerPage.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 { ImageKnifeComponent, ImageKnifeOption } from '@ohos/imageknife'; + +@Entry +@ComponentV2 +struct TestLoadCancelListenerPage { + @Local currentWidth: number = 200 + @Local currentHeight: number = 200 + @Local showChild: boolean = true; + @Local text: string = ""; + @Local ImageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ + loadSrc: "", + objectFit: ImageFit.Contain, + border: { radius: 50 } + }) + + build() { + Column() { + Text($r('app.string.onLoadCancel_reason', this.text)).margin(20).fontSize(15) + Button($r('app.string.rm_component_of_net')) + .margin(20) + .onClick(() => { + this.ImageKnifeOption = new ImageKnifeOption({ + loadSrc: "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/76/v3/EyF6z4FISpCHhae38eEexw/OtyAiu-zSSevNQYvUdtVmA.jpg", + objectFit: ImageFit.Contain, + onLoadListener: { + onLoadStart: () => { + this.showChild = false; + }, + onLoadCancel: (res) => { + this.text = res + console.log("TestLoadCancelListenerPage----onLoadCancel> url:" + res) + } + }, + border: { radius: 50 } + }) + }) + + Button($r('app.string.component_display')) + .margin(20).onClick(() => { + this.text = ""; + this.showChild = true; + this.ImageKnifeOption = new ImageKnifeOption({ + loadSrc: "", + objectFit: ImageFit.Contain, + border: { radius: 50 } + }) + }) + Button($r('app.string.rm_component_of_local')) + .margin(20) + .onClick(() => { + this.ImageKnifeOption = new ImageKnifeOption({ + loadSrc: $r('app.media.loading'), + objectFit: ImageFit.Contain, + onLoadListener: { + onLoadStart: () => { + this.showChild = false; + }, + onLoadCancel: (res) => { + this.text = res + console.log("TestLoadCancelListenerPage----onLoadCancel> url:" + res) + } + }, + border: { radius: 50 } + }) + }) + + if (this.showChild) { + ImageKnifeComponent( + { imageKnifeOption: this.ImageKnifeOption }) + .height(150) + .width(150) + .backgroundColor(Color.Orange) + .margin({ top: 20 }) + } + } + .height('100%') + .width('100%') + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/TestPrefetchToFileCache.ets b/entry/src/main/ets/pages/TestPrefetchToFileCache.ets index eb735a7..c541bd1 100644 --- a/entry/src/main/ets/pages/TestPrefetchToFileCache.ets +++ b/entry/src/main/ets/pages/TestPrefetchToFileCache.ets @@ -15,30 +15,30 @@ import { ImageKnifeComponent,ImageKnife,ImageKnifeOption } from '@ohos/libraryimageknife' @Entry -@Component +@ComponentV2 struct TestPrefetchToFileCachePage { - @State imageKnifeOption: ImageKnifeOption = { + @Local imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc:$r('app.media.startIcon'), placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed') - } + }) async preload(url:string) { let fileCachePath = await ImageKnife.getInstance().preLoadCache(url) console.log("preload-fileCachePath=="+ fileCachePath) } async preload1(url:string) { - let fileCachePath = await ImageKnife.getInstance().preLoadCache({ loadSrc: url }) + let fileCachePath = await ImageKnife.getInstance().preLoadCache(new ImageKnifeOption({ loadSrc: url })) console.log("preload-fileCachePath1=="+ fileCachePath) } build() { Column() { - Button("url预加载图片到文件缓存").margin({top:10}).onClick(async ()=>{ + Button($r('app.string.Preloading_images_to_file_cache_using_URL')).margin({top:10}).onClick(async ()=>{ await this.preload("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658") }) - Button("option预加载图片到文件缓存").margin({top:10}).onClick(async ()=>{ + Button($r('app.string.Preloading_images_to_file_cache_using_option')).margin({top:10}).onClick(async ()=>{ await this.preload1("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658") }) - Button("加载图片(预加载后可断网加载)").margin({top:10}).onClick(()=>{ + Button($r('app.string.Load_image_offline_after_preloading')).margin({top:10}).onClick(()=>{ this.imageKnifeOption.loadSrc = "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658" }) ImageKnifeComponent({ diff --git a/entry/src/main/ets/pages/TestRemoveCache.ets b/entry/src/main/ets/pages/TestRemoveCache.ets index 13e7def..aadfc5a 100644 --- a/entry/src/main/ets/pages/TestRemoveCache.ets +++ b/entry/src/main/ets/pages/TestRemoveCache.ets @@ -15,26 +15,26 @@ import { ImageKnifeComponent, ImageKnife, ImageKnifeOption, CacheStrategy } from '@ohos/libraryimageknife' @Entry -@Component +@ComponentV2 struct TestRemoveCache { - @State imageKnifeOption: ImageKnifeOption = { + @Local imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r('app.media.startIcon'), placeholderSrc: $r('app.media.loading'), errorholderSrc:$r('app.media.failed') - } - @State source: PixelMap | string | Resource = $r("app.media.startIcon"); - @State source1: PixelMap | string | Resource = $r("app.media.startIcon"); - @State url: string = ''; + }) + @Local source: PixelMap | string | Resource = $r("app.media.startIcon"); + @Local source1: PixelMap | string | Resource = $r("app.media.startIcon"); + @Local url: string = ''; build() { Column() { Flex() { - Button("预加载gif图").onClick(() => { + Button($r('app.string.Preloading_GIF')).onClick(() => { this.imageKnifeOption.loadSrc = "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658"; this.url = "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658"; }) .margin({left:10}) - Button("内存缓存获取gif").onClick(() => { + Button($r('app.string.Retrieve_GIF_from_memory')).onClick(() => { ImageKnife.getInstance() .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", CacheStrategy.Memory) @@ -43,7 +43,7 @@ struct TestRemoveCache { }) }) .margin({left:10}) - Button("文件缓存获取gif").onClick(() => { + Button($r('app.string.Retrieve_GIF_from_disk')).onClick(() => { ImageKnife.getInstance() .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", CacheStrategy.File) @@ -55,12 +55,12 @@ struct TestRemoveCache { } Flex() { - Button("预加载静态图").onClick(() => { + Button($r('app.string.Preloading_static_images')).onClick(() => { this.imageKnifeOption.loadSrc = 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp'; this.url = 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp'; }) .margin({left:10}) - Button("内存缓存获取").onClick(() => { + Button($r('app.string.Retrieve_images_from_memory')).onClick(() => { ImageKnife.getInstance() .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', CacheStrategy.Memory) @@ -69,7 +69,7 @@ struct TestRemoveCache { }) }) .margin({left:10}) - Button("文件缓存获取").onClick(() => { + Button($r('app.string.Retrieve_images_from_disk')).onClick(() => { ImageKnife.getInstance() .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', CacheStrategy.File) @@ -81,19 +81,19 @@ struct TestRemoveCache { }.margin({ top: 10 }) Flex() { - Button("删除全部缓存").onClick(() => { + Button($r('app.string.Delete_all_caches')).onClick(() => { ImageKnife.getInstance() .removeAllMemoryCache() ImageKnife.getInstance() .removeAllFileCache() }) .margin({left:5}) - Button("删除全部内存缓存").onClick(() => { + Button($r('app.string.Delete_all_memory_caches')).onClick(() => { ImageKnife.getInstance() .removeAllMemoryCache() }) .margin({left:5}) - Button("删除全部文件缓存").onClick(() => { + Button($r('app.string.Delete_all_file_caches')).onClick(() => { ImageKnife.getInstance() .removeAllFileCache() }) @@ -101,12 +101,12 @@ struct TestRemoveCache { }.margin({ top: 10 }) Flex() { - Button("删除自定义内存缓存").onClick(() => { + Button($r('app.string.Delete_all_custom_memory_caches')).onClick(() => { ImageKnife.getInstance() .removeMemoryCache(this.url) }) .margin({left:20}) - Button("删除自定义文件缓存").onClick(() => { + Button($r('app.string.Delete_all_custom_file_caches')).onClick(() => { ImageKnife.getInstance() .removeFileCache(this.url) }) diff --git a/entry/src/main/ets/pages/TestSetCustomImagePage.ets b/entry/src/main/ets/pages/TestSetCustomImagePage.ets index 809769f..58a5c7b 100644 --- a/entry/src/main/ets/pages/TestSetCustomImagePage.ets +++ b/entry/src/main/ets/pages/TestSetCustomImagePage.ets @@ -15,12 +15,17 @@ import { ImageKnifeComponent, ImageKnife, ImageKnifeOption } from '@ohos/libraryimageknife' @Entry -@Component +@ComponentV2 struct TestSetCustomImagePage { - @State imageKnifeOption: ImageKnifeOption = { + + getResourceString(res:Resource){ + return getContext().resourceManager.getStringSync(res.id) + } + + @Local imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r('app.media.startIcon'), placeholderSrc: $r('app.media.loading') - } + }) aboutToAppear(): void { ImageKnife.getInstance().setCustomGetImage(custom) } @@ -29,23 +34,23 @@ struct TestSetCustomImagePage { } build() { Column() { - Button("自定义下载a").onClick(()=>{ - this.imageKnifeOption = { + Button(this.getResourceString($r('app.string.Custom_network_download')) + " a").onClick(()=>{ + this.imageKnifeOption = new ImageKnifeOption({ loadSrc: "aaa", placeholderSrc: $r('app.media.loading') - } + }) }) - Button("自定义下载b").onClick(()=>{ - this.imageKnifeOption = { + Button(this.getResourceString($r('app.string.Custom_network_download')) + " b").onClick(()=>{ + this.imageKnifeOption = new ImageKnifeOption({ loadSrc: "bbb", placeholderSrc: $r('app.media.loading') - } + }) }) - Button("自定义下载c").onClick(()=>{ - this.imageKnifeOption = { + Button(this.getResourceString($r('app.string.Custom_network_download')) + " c").onClick(()=>{ + this.imageKnifeOption = new ImageKnifeOption({ loadSrc: "ccc", placeholderSrc: $r('app.media.loading') - } + }) }) ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption diff --git a/entry/src/main/ets/pages/TestTaskResourcePage.ets b/entry/src/main/ets/pages/TestTaskResourcePage.ets new file mode 100644 index 0000000..d5dc143 --- /dev/null +++ b/entry/src/main/ets/pages/TestTaskResourcePage.ets @@ -0,0 +1,79 @@ +/* + * 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" + +@ComponentV2 +export struct ZuImage { + @Param @Require src: PixelMap | ResourceStr | string | undefined + @Param @Require placeholderSrc: PixelMap | ResourceStr | string | undefined + @Local errorholderSrc: PixelMap | ResourceStr | string | undefined + + build() { + if (this.src) { + //当前版本存在bug + ImageKnifeComponent({ + imageKnifeOption: new ImageKnifeOption({ + loadSrc: this.src, + placeholderSrc: this.placeholderSrc, + errorholderSrc: this.errorholderSrc ?? this.placeholderSrc, + objectFit: ImageFit.Cover + }) + }) + } else { + Image(this.placeholderSrc) + .objectFit(ImageFit.Cover) + } + } +} + + +@Entry +@ComponentV2 +struct TestTaskResourcePage { + @Local stateMenus: Array = [ + "https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB", + 'https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/bf/v3/lSjrRwFcS-ez6jp1ALSQFg/0n7R7XinSPyrYLqDu_1dfw.jpg', + 'https://img-blog.csdn.net/20140514114029140', + 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', + ] + @Builder + _buildItem(item: string) { + Column({ space: 8 }) { + ZuImage({ + src: item, + placeholderSrc: $r("app.media.loading") + }).width(44) + .height(44) + } + } + build() { + + Grid() { + ForEach(this.stateMenus, (item: string) => { + GridItem() { + this._buildItem(item) + } + }) + }.width("100%") + .columnsTemplate('1fr 1fr 1fr 1fr 1fr') + .rowsGap(24) + .padding({ + top: 24, + bottom: 24, + left: 24, + right: 24 + }) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/TestWriteCacheStage.ets b/entry/src/main/ets/pages/TestWriteCacheStage.ets index 6b0b203..a5f0431 100644 --- a/entry/src/main/ets/pages/TestWriteCacheStage.ets +++ b/entry/src/main/ets/pages/TestWriteCacheStage.ets @@ -15,55 +15,55 @@ import { ImageKnifeComponent,CacheStrategy,ImageKnifeOption } from '@ohos/libraryimageknife' @Entry -@Component +@ComponentV2 struct TestWriteCacheStage { - @State imageKnifeOption1: ImageKnifeOption = { + @Local imageKnifeOption1: ImageKnifeOption = new ImageKnifeOption({ loadSrc:$r('app.media.startIcon'), placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed') - } - @State imageKnifeOption2: ImageKnifeOption = { + }) + @Local imageKnifeOption2: ImageKnifeOption = new ImageKnifeOption({ loadSrc:$r('app.media.startIcon'), placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed') - } - @State imageKnifeOption3: ImageKnifeOption = { + }) + @Local imageKnifeOption3: ImageKnifeOption = new ImageKnifeOption({ loadSrc:$r('app.media.startIcon'), placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed') - } + }) build() { Column() { - Button("写入内存文件缓存").margin({top:10}).onClick(async ()=>{ - this.imageKnifeOption1 = { + Button($r('app.string.Write_memory_and_file')).margin({top:10}).onClick(async ()=>{ + this.imageKnifeOption1 = new ImageKnifeOption({ loadSrc:'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed'), writeCacheStrategy:CacheStrategy.Default - } + }) }) ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).width(200).height(200).margin({top:10}) - Button("写入内存缓存").margin({top:10}).onClick(async ()=>{ - this.imageKnifeOption2 = { + Button($r('app.string.Write_memory')).margin({top:10}).onClick(async ()=>{ + this.imageKnifeOption2 = new ImageKnifeOption({ loadSrc:"https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB", placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed'), writeCacheStrategy:CacheStrategy.Memory - } + }) }) ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption2 }).width(200).height(200).margin({top:10}) - Button("写入文件缓存").margin({top:10}).onClick(async ()=>{ - this.imageKnifeOption3 = { + Button($r('app.string.Write_file')).margin({top:10}).onClick(async ()=>{ + this.imageKnifeOption3 = new ImageKnifeOption({ loadSrc:'https://img-blog.csdn.net/20140514114029140', placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed'), writeCacheStrategy:CacheStrategy.File - } + }) }) ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption3 diff --git a/entry/src/main/ets/pages/TransformPage.ets b/entry/src/main/ets/pages/TransformPage.ets index 7674156..0288c1d 100644 --- a/entry/src/main/ets/pages/TransformPage.ets +++ b/entry/src/main/ets/pages/TransformPage.ets @@ -17,29 +17,29 @@ import matrix4 from '@ohos.matrix4' @Entry -@Component +@ComponentV2 struct TransformPage { private custom_scale:number = 1 - @State matrix1:object = matrix4.identity().scale({ x: 1, y: 1 }) - @State ImageKnifeOption: ImageKnifeOption = { + @Local matrix1:object = matrix4.identity().scale({ x: 1, y: 1 }) + @Local ImageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r("app.media.rabbit"), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Contain, border: { radius: 50 } - } + }) build() { Column() { ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption }).height(200).width(200) .transform(this.matrix1) // Image($r('app.media.rabbit')).objectFit(ImageFit.Contain).height(200).width(200).transform(this.matrix1) - Button("放大").onClick(()=>{ + Button($r('app.string.Enlarge')).onClick(()=>{ this.custom_scale = this.custom_scale * 2 this.matrix1 = matrix4.identity().scale({ x: this.custom_scale, y: this.custom_scale }) }) - Button("缩小").onClick(()=>{ + Button($r('app.string.Reduce')).onClick(()=>{ this.custom_scale = this.custom_scale / 2 this.matrix1 = matrix4.identity().scale({ x: this.custom_scale, y: this.custom_scale }) }) diff --git a/entry/src/main/ets/pages/User.ets b/entry/src/main/ets/pages/User.ets index 1e3e582..603b767 100644 --- a/entry/src/main/ets/pages/User.ets +++ b/entry/src/main/ets/pages/User.ets @@ -15,24 +15,50 @@ import { ImageKnifeComponent, ImageKnifeOption } from '@ohos/libraryimageknife' // const logger = new imUtils.logger.IMLogger('Avatar') - -class MyImageOption extends ImageKnifeOption { - account?: string +@ObservedV2 +export class MyStorage { + static instance:MyStorage | undefined = undefined + static getInstance(){ + if(MyStorage.instance == undefined) { + MyStorage.instance = new MyStorage() + } + return MyStorage.instance + } + @Trace WeLink_Mob_fontSize_multiple: number = 1 } -@Component +@ComponentV2 export struct UserAvatar { - @Prop @Watch('userInfoUpdate') userInfo: string = "" // @Prop userInfo: string = "" imgSize: number = 100 radius: number = 12 borderSize: number = 0 imgSizes: number = 1 - @State ImageKnifeOption: ImageKnifeOption = new ImageKnifeOption() - @StorageProp('WeLink_Mob_fontSize_multiple') @Watch('updateImgSize') WeLink_Mob_fontSize_multiple: number = 0 + @Local ImageKnifeOption: ImageKnifeOption = new ImageKnifeOption() scalable: boolean = true; - @State calcImgSize: number = 100 - + @Local calcImgSize: number = 100 + @Param userInfo: string = "" + @Monitor('userInfo') + userInfoUpdate() { + // if (uri === 'userInfo' && this.imageKnifeOption.account !== this.userInfo.contactId) return; + // // logger.info(`userInfoUpdate uri=${uri} oldAcc=${this.imageKnifeOption.loadSrc} nowAcc=${this.userInfo.externalHeadUrl}`) + // if (this.userInfo.externalHeadUrl === this.imageKnifeOption.loadSrc && this.userInfo.infoUpdateTime.getTime() + // .toString() === this.imageKnifeOption?.signature?.getKey()) return; + this.ImageKnifeOption = new ImageKnifeOption({ + //TODO:写死loadSRC,场景:变更组件大小,所有图片不显示 + loadSrc: this.userInfo, + placeholderSrc: $r('app.media.loading'), + errorholderSrc: $r('app.media.failed'), + border: { radius:20,width:5,color:$r('app.color.start_window_background') }, + objectFit:ImageFit.Contain + // signature: new ObjectKey(this.userInfo.infoUpdateTime.getTime().toString()) + }) + } + @Local storage: MyStorage = MyStorage.getInstance() + @Monitor('storage.WeLink_Mob_fontSize_multiple') + updateImgSize() { + this.setImageSize() + } aboutToAppear(): void { this.userInfoUpdate() this.setImageSize() @@ -41,39 +67,19 @@ export struct UserAvatar { setImageSize() { if (!this.scalable) { this.calcImgSize = this.imgSize - } else if (this.WeLink_Mob_fontSize_multiple < 0.9) { + } else if (this.storage.WeLink_Mob_fontSize_multiple < 0.9) { this.calcImgSize = this.imgSize * 0.9 - } else if (this.WeLink_Mob_fontSize_multiple > 1.6) { + } else if (this.storage.WeLink_Mob_fontSize_multiple > 1.6) { this.calcImgSize = this.imgSize * 1.6 } else { - this.calcImgSize = this.imgSize * this.WeLink_Mob_fontSize_multiple + this.calcImgSize = this.imgSize * this.storage.WeLink_Mob_fontSize_multiple } } - updateImgSize() { - this.setImageSize() - } - aboutToReuse(param: ESObject) { this.userInfoUpdate() } - userInfoUpdate() { - // if (uri === 'userInfo' && this.imageKnifeOption.account !== this.userInfo.contactId) return; - // // logger.info(`userInfoUpdate uri=${uri} oldAcc=${this.imageKnifeOption.loadSrc} nowAcc=${this.userInfo.externalHeadUrl}`) - // if (this.userInfo.externalHeadUrl === this.imageKnifeOption.loadSrc && this.userInfo.infoUpdateTime.getTime() - // .toString() === this.imageKnifeOption?.signature?.getKey()) return; - this.ImageKnifeOption = { - //TODO:写死loadSRC,场景:变更组件大小,所有图片不显示 - loadSrc: this.userInfo, - placeholderSrc: $r('app.media.loading'), - errorholderSrc: $r('app.media.failed'), - border: { radius:20,width:5,color:$r('app.color.start_window_background') }, - objectFit:ImageFit.Contain - // signature: new ObjectKey(this.userInfo.infoUpdateTime.getTime().toString()) - } - } - build() { Row() { // Image(this.imageKnifeOption.loadSrc) diff --git a/entry/src/main/ets/pages/UserPage.ets b/entry/src/main/ets/pages/UserPage.ets index ee210bc..92d1caf 100644 --- a/entry/src/main/ets/pages/UserPage.ets +++ b/entry/src/main/ets/pages/UserPage.ets @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { UserAvatar } from './User' +import { MyStorage, UserAvatar } from './User' class CommonDataSource implements IDataSource { private dataArray: T[] = [] @@ -55,56 +55,72 @@ class CommonDataSource implements IDataSource { } } @Entry -@Component +@ComponentV2 struct Index { - @State hotCommendList:CommonDataSource = new CommonDataSource([]) + @Local hotCommendList:CommonDataSource = new CommonDataSource([]) private data:string[] = [ - "http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg", - "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg", - "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg", - "http://g.hiphotos.baidu.com/image/pic/item/55e736d12f2eb938d5277fd5d0628535e5dd6f4a.jpg", - "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg", - "http://b.hiphotos.baidu.com/image/pic/item/9d82d158ccbf6c81b94575cfb93eb13533fa40a2.jpg", - "http://e.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c1badd5a685d6277f9e2ff81e.jpg", - "http://g.hiphotos.baidu.com/image/pic/item/0d338744ebf81a4c87a3add4d52a6059252da61e.jpg", - "http://a.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee5080c8142ff5e0fe99257e19.jpg", - "http://f.hiphotos.baidu.com/image/pic/item/4034970a304e251f503521f5a586c9177e3e53f9.jpg", - "http://b.hiphotos.baidu.com/image/pic/item/279759ee3d6d55fbb3586c0168224f4a20a4dd7e.jpg", - "http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg", - "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg", - "http://c.hiphotos.baidu.com/image/pic/item/9c16fdfaaf51f3de1e296fa390eef01f3b29795a.jpg", - "http://d.hiphotos.baidu.com/image/pic/item/b58f8c5494eef01f119945cbe2fe9925bc317d2a.jpg", - "http://h.hiphotos.baidu.com/image/pic/item/902397dda144ad340668b847d4a20cf430ad851e.jpg", - "http://b.hiphotos.baidu.com/image/pic/item/359b033b5bb5c9ea5c0e3c23d139b6003bf3b374.jpg", - "http://a.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a292d2472199d25bc315d607c7c.jpg", - "http://b.hiphotos.baidu.com/image/pic/item/e824b899a9014c08878b2c4c0e7b02087af4f4a3.jpg", - "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/56/v3/8MdhfSsCSMKj4sA6okUWrg/5uBx56tLTUO3RYQl-E5JiQ.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/e2/v3/4zI1Xm_3STmV30aZXWRrKw/6aN7WodDRUiBApgffiLPCg.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/42/v3/2dSQCqERTP2TTPyssOMEbQ/zL1ebnKKQ_ilqTDcwCAkOw.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/78/v3/qQJpAtRGQe2e_VhbGHDgIw/b3zlit99S6GybD3XdNwqJw.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/55/v3/5DZ2LLqYSsK85-shqgLveQ/7ZXcyCWNTvOzQP5FFLBGkg.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/3e/v3/LqRoLI-PRSu9Nqa8KdJ-pQ/dSqskBpSR9eraAMn7NBdqA.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/25/v3/jgB2ekkTRX-3yTYZalnANQ/xff_x9cbSPqb7fbNwgJa7A.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/fb/v3/alXwXLHKSyCAIWt_ydgD2g/BCCuu25TREOitQxM7eYOEw.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/63/v3/qbe6NZkCQyGcITvdWoZBgg/Y-5U1z3GT_yaK8CBD3jkwg.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/16/v3/fm2tO4TsRH6mv_D_nSSd5w/FscLpLwQQ-KuV7oaprFK2Q.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/89/v3/UAUvtPHqRD-GWWANsEC57Q/zcRJCQebQ322Aby4jzmwmQ.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/30/v3/tUUzzx73R4yp8G--lMhuWQ/EBbcu_dLTT-Jj68XAh6mtA.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/76/v3/EyF6z4FISpCHhae38eEexw/OtyAiu-zSSevNQYvUdtVmA.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/37/v3/12rH1yiEQmK9wlOOcy5avQ/RzBXiEBRRqOC7LRkwNj6VA.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/9a/v3/TpRN4AIzRoyUXIqWdKoE0g/ShOnD_tfS46HDbpSWhbCkQ.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/03/v3/H3X17s8eTdS2w56JgbB5jQ/a45sT-j8Sbe8sSQXTzeYvQ.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/10/v3/qaEzwkU0QeKb1yehnP2Xig/q7fxAlgMQKup-HUBayRLGQ.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/96/v3/rMJJoAflTDSWa1z2pHs2wg/8dOqD0GlQBOCL5AvQok9FQ.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/ed/v3/KMO4D6D2QGuVOCLX4AhOFA/ef51xAaLQuK7BsnuD9abog.jpg", + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/d9/v3/FSZH0aTdSqWxeAaxoPvi0g/RqxPxUCXQFiTMBfKTF9kkw.jpg", "https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB", - 'https://img-blog.csdnimg.cn/20191215043500229.png', + 'https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/bf/v3/lSjrRwFcS-ez6jp1ALSQFg/0n7R7XinSPyrYLqDu_1dfw.jpg', 'https://img-blog.csdn.net/20140514114029140', 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', ] aboutToAppear(): void { - this.hotCommendList.addData(this.hotCommendList.totalCount(),this.data) - AppStorage.set("WeLink_Mob_fontSize_multiple",1) + MyStorage.getInstance().WeLink_Mob_fontSize_multiple = 1 } build() { Column() { Button("bigger").onClick(()=>{ - AppStorage.set("WeLink_Mob_fontSize_multiple",1.6) + MyStorage.getInstance().WeLink_Mob_fontSize_multiple = 1.6 }) Button("small").onClick(()=>{ - AppStorage.set("WeLink_Mob_fontSize_multiple",0.8) + MyStorage.getInstance().WeLink_Mob_fontSize_multiple = 0.8 }) List(){ - LazyForEach(this.hotCommendList,(item:string)=>{ - ListItem(){ - ReuseImage({ - userInfo:item - }).width("100%").height("100%").backgroundColor(Color.Yellow) - }.width(200).height(200).margin({bottom:5}) - }) + // LazyForEach(this.hotCommendList,(item:string)=>{ + // ListItem(){ + // ReuseImage({ + // userInfo:item + // }).width("100%").height("100%").backgroundColor(Color.Yellow) + // }.width(200).height(200).margin({bottom:5}) + // }) + Repeat(this.data) + .each((repeatItem)=>{ + ListItem(){ + ReuseImage({ + userInfo:repeatItem.item + }).width("100%").height("100%").backgroundColor(Color.Yellow) + }.width(200).height(200).margin({bottom:5}).key("reuse") + }) + .key(item => item+"reuse") + .virtualScroll() + .template("1",(repeatItem)=>{ + ListItem(){ + ReuseImage({ + userInfo:repeatItem.item + }).width("100%").height("100%").backgroundColor(Color.Yellow) + }.width(200).height(200).margin({bottom:5}).key("reuse") + }) } // .cachedCount(20) .width("100%") @@ -115,13 +131,13 @@ struct Index { } -@Reusable -@Component +// @Reusable +@ComponentV2 struct ReuseImage { - @State userInfo:string = "" - aboutToReuse(params: ESObject): void { - this.userInfo = params.userInfo - } + @Param userInfo:string = "" + // aboutToReuse(params: ESObject): void { + // this.userInfo = params.userInfo + // } build() { Column(){ diff --git a/entry/src/main/ets/pages/dataShareUriLoadPage.ets b/entry/src/main/ets/pages/dataShareUriLoadPage.ets index bb71ac2..7392044 100644 --- a/entry/src/main/ets/pages/dataShareUriLoadPage.ets +++ b/entry/src/main/ets/pages/dataShareUriLoadPage.ets @@ -18,23 +18,23 @@ import { photoAccessHelper } from '@kit.MediaLibraryKit'; @Entry -@Component +@ComponentV2 struct DataShareUriLoadPage { - @State imageKnifeOption1: ImageKnifeOption = - { + @Local imageKnifeOption1: ImageKnifeOption = + new ImageKnifeOption({ loadSrc: $r('app.media.icon'), placeholderSrc: $r('app.media.loading'), errorholderSrc: $r('app.media.failed') - }; + }); build() { Scroll() { Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { - Text("获取媒体图库的uri用ImageKnife展示").fontSize(15) + Text($r('app.string.Retrieve_media_gallery')).fontSize(15) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { - Button("点击加载Uri并展示") + Button($r('app.string.Click_load_Uri')) .onClick(async () => { let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions(); photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; @@ -43,10 +43,10 @@ struct DataShareUriLoadPage { let photoViewPicker = new photoAccessHelper.PhotoViewPicker(); let photoSelectResult: photoAccessHelper.PhotoSelectResult = await photoViewPicker.select(photoSelectOptions); uris = photoSelectResult.photoUris; - this.imageKnifeOption1 = { + this.imageKnifeOption1 = new ImageKnifeOption({ loadSrc: uris[0], placeholderSrc:$r('app.media.loading') - } + }) }).margin({ top: 5, left: 3 }) ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).width(300).height(300) }.width('100%').backgroundColor(Color.Pink) diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 index 9036eb0..6c284c7 100644 --- a/entry/src/main/module.json5 +++ b/entry/src/main/module.json5 @@ -5,9 +5,8 @@ "description": "$string:module_desc", "mainElement": "EntryAbility", "deviceTypes": [ - "phone", - "tablet", - "2in1" + "default", + "tablet" ], "deliveryWithInstall": true, "installationFree": false, diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json index 13c1fec..c16a7d9 100644 --- a/entry/src/main/resources/base/element/string.json +++ b/entry/src/main/resources/base/element/string.json @@ -19,6 +19,498 @@ { "name": "app_permission_READ_IMAGEVIDEO", "value": "获取读媒体资源权限" + }, + { + "name": "Test_ImageAnimator", + "value": "Test ImageAnimator component" + }, + { + "name": "Test_multiple_images", + "value": "Test loading multiple identical images" + }, + { + "name": "Test_Task_error", + "value": "Test placeholder map Task error" + }, + { + "name": "Test_HSP", + "value": "Test HSP scene preloading" + }, + { + "name": "Test_SingleImage", + "value": "SingleImage" + }, + { + "name": "Test_custom_download", + "value": "Global custom download" + }, + { + "name": "Multiple_images", + "value": "Multiple images" + }, + { + "name": "Display_long_image", + "value": "Display long image" + }, + { + "name": "Image_scaling", + "value": "Image scaling" + }, + { + "name": "Message_list", + "value": "Message list" + }, + { + "name": "Custom_cache_key", + "value": "Custom cache key" + }, + { + "name": "Preloading_images_to_cache", + "value": "Preloading images to file cache" + }, + { + "name": "Retrieve_image_display_from_cache", + "value": "Retrieve image display from cache" + }, + { + "name": "Test_single_request_header", + "value": "Test a single request header" + }, + { + "name": "Test_write_cache_strategy", + "value": "Test write cache strategy" + }, + { + "name": "Image_Transformation", + "value": "Image Transformation" + }, + { + "name": "Different_ObjectFit", + "value": "Different ObjectFit" + }, + { + "name": "Test_image_loading_success_or_failure_events", + "value": "Test image loading success/failure events" + }, + { + "name": "Test_removing_image_cache_interface", + "value": "Test removing image cache interface" + }, + { + "name": "Test_error_image_display", + "value": "Test error image display" + }, + { + "name": "Test_media_URL", + "value": "Test media URL" + }, + { + "name": "Display_the_first_frame", + "value": "Display the first frame of the animation" + }, + { + "name": "Display_the_last_frame", + "value": "Display the last frame of the animation" + }, + { + "name": "Play", + "value": "Play" + }, + { + "name": "Pause", + "value": "Pause" + }, + { + "name": "Stop", + "value": "Stop" + }, + { + "name": "Infinite_loop", + "value": "Infinite loop" + }, + { + "name": "Play_once", + "value": "Play once" + }, + { + "name": "Play_twice", + "value": "Play twice" + }, + { + "name": "Local_SVG", + "value": "Local SVG image" + }, + { + "name": "Under_context_file", + "value": "Files under context file" + }, + { + "name": "Network_images", + "value": "Network images" + }, + { + "name": "Custom_network_download", + "value": "Custom network download" + }, + { + "name": "PixelMap_loads_images", + "value": "PixelMap loads images" + }, + { + "name": "Enlarge", + "value": "Enlarge" + }, + { + "name": "Reduce", + "value": "Reduce" + }, + { + "name": "Click_on_add", + "value": "Click on the size to add 50" + }, + { + "name": "Click_on_reduce", + "value": "Click to reduce size by 50" + }, + { + "name": "The_key_fixed_1", + "value": "The key is fixed at 1" + }, + { + "name": "The_key_changes_timestamp", + "value": "Key changes every time: timestamp" + }, + { + "name": "Load", + "value": "Load" + }, + { + "name": "Preloading_images_to_file_cache_using_URL", + "value": "Preloading images to file cache using URL" + }, + { + "name": "Preloading_images_to_file_cache_using_option", + "value": "Preloading images to file cache using option" + }, + { + "name": "Load_image_offline_after_preloading", + "value": "Load image (can be loaded offline after preloading)" + }, + { + "name": "Preloading_GIF", + "value": "Preloading GIF" + }, + { + "name": "Retrieve_GIF_from_memory", + "value": "Retrieve GIF from memory cache" + }, + { + "name": "Retrieve_GIF_from_disk", + "value": "Retrieve GIF from disk cache" + }, + { + "name": "Preloading_static_images", + "value": "Preloading static images" + }, + { + "name": "Retrieve_images_from_memory", + "value": "Retrieve images from memory cache" + }, + { + "name": "Retrieve_images_from_disk", + "value": "Retrieve images from memory disk" + }, + { + "name": "Write_memory_and_file", + "value": "Write to memory and file cache" + }, + { + "name": "Write_memory", + "value": "Write to memory cache" + }, + { + "name": "Write_file", + "value": "Write to file cache" + }, + { + "name": "Main_image_Fill", + "value": "Main image Fill Stretch Fill" + }, + { + "name": "Maintain_proportion_filling", + "value": "Maintain proportion filling in the placeholder map 'Include'" + }, + { + "name": "Error_graph_None", + "value": "Error graph None remains unchanged" + }, + { + "name": "Test_failure_success", + "value": "Test failure/success" + }, + { + "name": "Custom_download_failed", + "value": "Custom download failed" + }, + { + "name": "Retrieve_media_gallery", + "value": "Retrieve the URI of the media gallery and display it using ImageKnife" + }, + { + "name": "Click_load_Uri", + "value": "Click to load Uri and display" + }, + { + "name": "Delete_all_caches", + "value": "Delete all caches" + }, + { + "name": "Delete_all_memory_caches", + "value": "Delete all memory caches" + }, + { + "name": "Delete_all_file_caches", + "value": "Delete all file caches" + }, + { + "name": "Delete_all_custom_memory_caches", + "value": "Delete all custom memory caches" + }, + { + "name": "Delete_all_custom_file_caches", + "value": "Delete all custom file caches" + }, + { + "name": "Blur_effect", + "value": "Blur effect" + }, + { + "name": "Highlighting_effect", + "value": "Highlighting effect" + }, + { + "name": "Ashing_effect", + "value": "Ashing effect" + }, + { + "name": "Inverse_effect", + "value": "Inverse effect" + }, + { + "name": "Animation_filter_effect", + "value": "Animation filter effect" + }, + { + "name": "Crop_circular_effect", + "value": "Crop circular effect" + }, + { + "name": "Crop_circular_with_border_effect", + "value": "Crop circular with border effect" + }, + { + "name": "Contrast_effect", + "value": "Contrast effect" + }, + { + "name": "Black_ink_filtering_effect", + "value": "Black ink filtering effect" + }, + { + "name": "Rotate", + "value": "Rotate" + }, + { + "name": "Corners", + "value": "Corners" + }, + { + "name": "Kuwahara_Filter_effect", + "value": "Kuwahara filter effect" + }, + { + "name": "Pixelated_Filter_effect", + "value": "Pixelated filtering effect" + }, + { + "name": "Sketch_Filter_effect", + "value": "Sketch Filter effect" + }, + { + "name": "Distortion_Filter_effect", + "value": "Distortion Filter effect" + }, + { + "name": "Decorative_Filter_effect", + "value": "Decorative Filter effect" + }, + { + "name": "Square_cutting_effect", + "value": "Square cutting effect" + }, + { + "name": "Top_cutting_effect", + "value": "Top cutting effect" + }, + { + "name": "Middle_cutting_effect", + "value": "Middle cutting effect" + }, + { + "name": "Bottom_cutting_effect", + "value": "Bottom cutting effect" + }, + { + "name": "Mask_effect", + "value": "Mask effect" + }, + { + "name": "TIPS", + "value": "Please shut down the network first and ensure that there is no cache of images from this network in the test failure scenario locally" + }, + { + "name": "Image_Downsampling_Functionality", + "value": "Downscale Image effect" + }, + { + "name": "Sampling_pecification", + "value": "降采样规格" + }, + { + "name": "Unreal_samples", + "value": "未降采样大小" + }, + { + "name": "After_the_sampling", + "value": "降采样后大小" + }, + { + "name": "image_format", + "value": "picture format:%s" + }, + { + "name": "image_width", + "value": "image width:%d" + }, + { + "name": "image_height", + "value": "image height:%d" + }, + { + "name": "cur_cache_limit", + "value": "%s:current cache limit:%fM" + }, + { + "name": "cur_cache_image_num", + "value": "%s:current cache image number:%d" + }, + { + "name": "cur_cache_size", + "value": "%s:current cache size:%fM" + }, + { + "name": "load_memory", + "value": "memory loaded picture" + }, + { + "name": "load_disk", + "value": "disk cache loads images" + }, + { + "name": "get_cur_memory_limit", + "value": "gets the current memory cache upper limit" + }, + { + "name": "get_img_number_of_cache", + "value": "gets the number of images cached in memory" + }, + { + "name": "get_cur_memory_size", + "value": "gets the size of the current cache" + }, + { + "name": "get_cur_disk_limit", + "value": "Gets the current disk cache upper limit" + }, + { + "name": "get_img_number_of_disk", + "value": "gets the number of images cached on disk" + }, + { + "name": "get_cur_disk_size", + "value": "gets the size of the current disk" + }, + { + "name": "select_color_btn", + "value": "click to select the color you want to change" + }, + { + "name": "click_img_to_change_color", + "value": "click on the image to change the image color" + }, + { + "name": "test_non_svg_color", + "value": "test non-SVG images for color change" + }, + { + "name": "test_svg_color", + "value": "Test svg picture color change" + }, + { + "name": "red", + "value": "red" + }, + { + "name": "yellow", + "value": "yellow" + }, + { + "name": "green", + "value": "green" + }, + { + "name": "blue", + "value": "blue" + }, + { + "name": "master_image", + "value": "master image:" + }, + { + "name": "rm_component_of_net", + "value": "remove Component - Network load picture" + }, + { + "name": "rm_component_of_local", + "value": "remove Component - Local resource picture" + }, + { + "name": "component_display", + "value": "recovery component display" + }, + { + "name": "onLoadCancel_reason", + "value": "onLoadCancel callback reason:%s" + }, + { + "name": "test_cache_btn", + "value": "test data for in cache" + }, + { + "name": "test_change_color_btn", + "value": "test change color for image" + }, + { + "name": "test_cancel_callback_btn", + "value": "test callback of cancel" + }, + { + "name": "memory", + "value": "Memory" + }, + { + "name": "disk", + "value": "Disk" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/base/media/ic_test_change_color_png.png b/entry/src/main/resources/base/media/ic_test_change_color_png.png new file mode 100644 index 0000000..75f0a56 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_test_change_color_png.png differ diff --git a/entry/src/main/resources/base/media/ic_test_change_color_svg.svg b/entry/src/main/resources/base/media/ic_test_change_color_svg.svg new file mode 100644 index 0000000..39c69c2 --- /dev/null +++ b/entry/src/main/resources/base/media/ic_test_change_color_svg.svg @@ -0,0 +1,14 @@ + + + svg细图标 + + + + + + + + + + + \ 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 42b571c..623e12e 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -3,7 +3,6 @@ "pages/Index", "pages/ListPage", "pages/SingleImage", - "pages/ManyPhotoShowPage", "pages/LongImagePage", "pages/TransformPage", "pages/UserPage", @@ -22,6 +21,11 @@ "pages/TestCommonImage", "pages/ImageAnimatorPage", "pages/TestSetCustomImagePage", - "pages/TestErrorHolderPage" + "pages/TestErrorHolderPage", + "pages/TestTaskResourcePage", + "pages/DownSamplePage", + "pages/TestCacheDataPage", + "pages/TestChangeColorPage", + "pages/TestLoadCancelListenerPage" ] } \ No newline at end of file diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json index 6250115..5e5e284 100644 --- a/entry/src/main/resources/zh_CN/element/string.json +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -19,6 +19,494 @@ { "name": "app_permission_READ_IMAGEVIDEO", "value": "获取读媒体资源权限" + }, + { + "name": "Test_ImageAnimator", + "value": "测试ImageAnimator组件" + }, + { + "name": "Test_multiple_images", + "value": "测试加载多张相同图片" + }, + { + "name": "Test_Task_error", + "value": "测试占位图Task报错" + }, + { + "name": "Test_HSP", + "value": "测试HSP场景预加载" + }, + { + "name": "Test_SingleImage", + "value": "单个图片使用" + }, + { + "name": "Test_custom_download", + "value": "全局自定义下载" + }, + { + "name": "Multiple_images", + "value": "多图" + }, + { + "name": "Display_long_image", + "value": "长图显示" + }, + { + "name": "Image_scaling", + "value": "缩放图片" + }, + { + "name": "Message_list", + "value": "消息列表" + }, + { + "name": "Custom_cache_key", + "value": "自定义缓存key" + }, + { + "name": "Preloading_images_to_cache", + "value": "预加载图片到文件缓存" + }, + { + "name": "Retrieve_image_display_from_cache", + "value": "从缓存获取图片显示" + }, + { + "name": "Test_single_request_header", + "value": "测试单个请求头" + }, + { + "name": "Test_write_cache_strategy", + "value": "测试写入缓存策略" + }, + { + "name": "Image_Transformation", + "value": "图片变换" + }, + { + "name": "Different_ObjectFit", + "value": "不同的ObjectFit" + }, + { + "name": "Test_image_loading_success_or_failure_events", + "value": "测试图片加载成功/失败事件" + }, + { + "name": "Test_removing_image_cache_interface", + "value": "测试移除图片缓存接口" + }, + { + "name": "Test_error_image_display", + "value": "测试错误图显示" + }, + { + "name": "Display_the_first_frame", + "value": "动画显示第一帧" + }, + { + "name": "Display_the_last_frame", + "value": "动画显示最后一帧" + }, + { + "name": "Play", + "value": "播放" + }, + { + "name": "Pause", + "value": "暂停" + }, + { + "name": "Stop", + "value": "停止" + }, + { + "name": "Infinite_loop", + "value": "无限循环" + }, + { + "name": "Play_once", + "value": "播放一次" + }, + { + "name": "Play_twice", + "value": "播放两次" + }, + { + "name": "Local_SVG", + "value": "本地资源SVG图片" + }, + { + "name": "Under_context_file", + "value": "本地context files下文件" + }, + { + "name": "Network_images", + "value": "网络图片" + }, + { + "name": "Custom_network_download", + "value": "自定义下载" + }, + { + "name": "PixelMap_loads_images", + "value": "PixelMap加载图片" + }, + { + "name": "Enlarge", + "value": "放大" + }, + { + "name": "Reduce", + "value": "缩小" + }, + { + "name": "Click_on_add", + "value": "点击尺寸加50" + }, + { + "name": "Click_on_reduce", + "value": "点击尺寸减50" + }, + { + "name": "The_key_fixed_1", + "value": "key固定为 1" + }, + { + "name": "The_key_changes_timestamp", + "value": "key每次变化:时间戳" + }, + { + "name": "Load", + "value": "加载" + }, + { + "name": "Preloading_images_to_file_cache_using_URL", + "value": "url预加载图片到文件缓存" + }, + { + "name": "Preloading_images_to_file_cache_using_option", + "value": "option预加载图片到文件缓存" + }, + { + "name": "Load_image_offline_after_preloading", + "value": "加载图片(预加载后可断网加载)" + }, + { + "name": "Preloading_GIF", + "value": "预加载gif图" + }, + { + "name": "Retrieve_GIF_from_memory", + "value": "内存缓存获取gif" + }, + { + "name": "Retrieve_GIF_from_disk", + "value": "文件缓存获取gif" + }, + { + "name": "Preloading_static_images", + "value": "预加载静态图" + }, + { + "name": "Retrieve_images_from_memory", + "value": "内存缓存获取" + }, + { + "name": "Retrieve_images_from_disk", + "value": "文件缓存获取" + }, + { + "name": "Write_memory_and_file", + "value": "写入内存文件缓存" + }, + { + "name": "Write_memory", + "value": "写入内存缓存" + }, + { + "name": "Write_file", + "value": "写入文件缓存" + }, + { + "name": "Main_image_Fill", + "value": "主图Fill拉伸填充" + }, + { + "name": "Maintain_proportion_filling", + "value": "占位图Contain保持比例填充" + }, + { + "name": "Error_graph_None", + "value": "错误图None不变化" + }, + { + "name": "Test_failure_success", + "value": "测试失败/成功场景" + }, + { + "name": "Custom_download_failed", + "value": "自定义下载失败" + }, + { + "name": "Retrieve_media_gallery", + "value": "获取媒体图库的uri用ImageKnife展示" + }, + { + "name": "Click_load_Uri", + "value": "点击加载Uri并展示" + }, + { + "name": "Delete_all_caches", + "value": "删除全部缓存" + }, + { + "name": "Delete_all_memory_caches", + "value": "删除全部内存缓存" + }, + { + "name": "Delete_all_file_caches", + "value": "删除全部文件缓存" + }, + { + "name": "Delete_all_custom_memory_caches", + "value": "删除自定义内存缓存" + }, + { + "name": "Delete_all_custom_file_caches", + "value": "删除自定义文件缓存" + }, + { + "name": "Blur_effect", + "value": "模糊效果" + }, + { + "name": "Highlighting_effect", + "value": "高亮效果" + }, + { + "name": "Ashing_effect", + "value": "灰化效果" + }, + { + "name": "Inverse_effect", + "value": "反转效果" + }, + { + "name": "Animation_filter_effect", + "value": "动画滤镜效果" + }, + { + "name": "Crop_circular_effect", + "value": "裁剪圆形效果" + }, + { + "name": "Crop_circular_with_border_effect", + "value": "裁剪圆形带边框效果" + }, + { + "name": "Contrast_effect", + "value": "对比度效果" + }, + { + "name": "Black_ink_filtering_effect", + "value": "乌墨色滤波效果" + }, + { + "name": "Rotate", + "value": "旋转效果" + }, + { + "name": "Corners", + "value": "圆角效果" + }, + { + "name": "Kuwahara_Filter_effect", + "value": "桑原滤波效果" + }, + { + "name": "Pixelated_Filter_effect", + "value": "像素化滤波效果" + }, + { + "name": "Sketch_Filter_effect", + "value": "素描滤波效果" + }, + { + "name": "Distortion_Filter_effect", + "value": "扭曲滤波效果" + }, + { + "name": "Decorative_Filter_effect", + "value": "装饰滤波效果" + }, + { + "name": "Square_cutting_effect", + "value": "正方形裁剪效果" + }, + { + "name": "Top_cutting_effect", + "value": "上方裁剪效果" + }, + { + "name": "Middle_cutting_effect", + "value": "中间裁剪效果" + }, + { + "name": "Bottom_cutting_effect", + "value": "底下裁剪效果" + }, + { + "name": "Mask_effect", + "value": "遮罩效果" + }, + { + "name": "TIPS", + "value": "测试失败场景请先关闭网络,并保证本地没有此网络图片的缓存" + }, + { + "name": "Image_Downsampling_Functionality", + "value": "降采样功能" + }, + { + "name": "Sampling_pecification", + "value": "降采样规格" + }, + { + "name": "Unreal_samples", + "value": "未降采样大小" + }, + { + "name": "After_the_sampling", + "value": "降采样后大小" + }, + { + "name": "image_format", + "value": "图片格式:%s" + }, + { + "name": "image_width", + "value": "图片宽度:%d" + }, + { + "name": "image_height", + "value": "图片高度:%d" + }, + { + "name": "cur_cache_limit", + "value": "%s:当前缓存上限:%fM" + }, + { + "name": "cur_cache_image_num", + "value": "%s:当前缓存图片数量:%d" + }, + { + "name": "cur_cache_size", + "value": "%s:当前缓存的大小:%fM" + }, + { + "name": "load_memory", + "value": "内存加载图片" + }, + { + "name": "load_disk", + "value": "磁盘缓存加载图片" + }, + { + "name": "get_cur_memory_limit", + "value": "获取当前内存缓存上限" + }, + { + "name": "get_img_number_of_cache", + "value": "获取当前内存缓存图片数量" + }, + { + "name": "get_cur_memory_size", + "value": "获取当前缓存的大小" + }, + { + "name": "get_cur_disk_limit", + "value": "获取当前磁盘缓存上限" + }, + { + "name": "get_img_number_of_disk", + "value": "获取当前磁盘缓存图片数量" + }, + { + "name": "get_cur_disk_size", + "value": "获取当前磁盘的大小" + }, + { + "name": "select_color_btn", + "value": "点击选择要更改的颜色" + }, + { + "name": "click_img_to_change_color", + "value": "点击图片更改图片颜色" + }, + { + "name": "test_non_svg_color", + "value": "测试非svg图片变色" + }, + { + "name": "test_svg_color", + "value": "测试svg图片变色" + }, + { + "name": "red", + "value": "红色" + }, + { + "name": "yellow", + "value": "黄色" + }, + { + "name": "green", + "value": "绿色" + }, + { + "name": "blue", + "value": "蓝色" + }, + { + "name": "master_image", + "value": "原图:" + }, + { + "name": "rm_component_of_net", + "value": "移除组件-网络加载图片" + }, + { + "name": "rm_component_of_local", + "value": "移除组件-本地资源图片" + }, + { + "name": "component_display", + "value": "恢复组件显示" + }, + { + "name": "onLoadCancel_reason", + "value": "onLoadCancel回调原因:%s" + }, + { + "name": "test_cache_btn", + "value": "测试缓存数据" + }, + { + "name": "test_change_color_btn", + "value": "测试颜色变换" + }, + { + "name": "test_cancel_callback_btn", + "value": "测试加载取消回调接口" + }, + { + "name": "memory", + "value": "内存" + }, + { + "name": "disk", + "value": "磁盘" } ] } \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets index eb13a71..71dfa05 100644 --- a/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets +++ b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets @@ -14,8 +14,8 @@ */ import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; import { ImageKnifeOption, ImageKnifeRequest } from '@ohos/imageknife/Index'; -import { DefaultJobQueue } from '@ohos/imageknife/src/main/ets/utils/DefaultJobQueue'; -import { IJobQueue } from '@ohos/imageknife/src/main/ets/utils/IJobQueue'; +import { DefaultJobQueue } from '@ohos/imageknife/src/main/ets/queue/DefaultJobQueue'; +import { IJobQueue } from '@ohos/imageknife/src/main/ets/queue/IJobQueue'; import taskpool from '@ohos.taskpool'; import common from '@ohos.app.ability.common'; @@ -70,10 +70,10 @@ export default function DefaultJobQueueTest() { } function makeRequest(src: string, context: common.UIAbilityContext, priority?: taskpool.Priority): ImageKnifeRequest { - let option: ImageKnifeOption = { + let option: ImageKnifeOption = new ImageKnifeOption({ loadSrc: src, priority: priority - } + }) return new ImageKnifeRequest( option, context, diff --git a/entry/src/ohosTest/ets/test/FileLruCache.test.ets b/entry/src/ohosTest/ets/test/FileLruCache.test.ets index 2dd76f3..ed4d183 100644 --- a/entry/src/ohosTest/ets/test/FileLruCache.test.ets +++ b/entry/src/ohosTest/ets/test/FileLruCache.test.ets @@ -17,7 +17,7 @@ import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from import Constants from '../../../main/ets/common/Constants'; import taskpool from '@ohos.taskpool'; import { GlobalContext } from '../../../main/ets/common/GlobalContext'; -import { FileCache } from '@ohos/imageknife/src/main/ets/utils/FileCache'; +import { FileCache } from '@ohos/imageknife/src/main/ets/cache/FileCache'; import { IEngineKey, ImageKnifeOption } from '@ohos/imageknife'; import { DefaultEngineKey } from '@ohos/imageknife/src/main/ets/key/DefaultEngineKey'; @@ -153,9 +153,9 @@ export default function FileLruCacheTest() { }); it('fileCacheEngineKey', 0, () => { let engineKey: IEngineKey = new DefaultEngineKey() - let imageKnifeOption: ImageKnifeOption = { + let imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc:"abc" - } + }) let imageKey = engineKey.generateFileKey(imageKnifeOption.loadSrc,"") let imageAnimatorKey = engineKey.generateFileKey(imageKnifeOption.loadSrc,"",true) expect(imageKey == imageAnimatorKey).assertFalse() diff --git a/entry/src/ohosTest/ets/test/ImageKnife.test.ets b/entry/src/ohosTest/ets/test/ImageKnife.test.ets index 8c6811f..7df3605 100644 --- a/entry/src/ohosTest/ets/test/ImageKnife.test.ets +++ b/entry/src/ohosTest/ets/test/ImageKnife.test.ets @@ -40,10 +40,10 @@ export default function ImageKnifeTest() { it('removeMemoryCache', 0, async () => { let a = 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp'; - let option: ImageKnifeOption = { + let option: ImageKnifeOption = new ImageKnifeOption({ loadSrc: 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', signature: '' - } + }) let key = ImageKnife.getInstance() .getEngineKeyImpl() .generateMemoryKey('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', ImageKnifeRequestSource.SRC, option) diff --git a/entry/src/ohosTest/ets/test/ImageKnifeOption.test.ets b/entry/src/ohosTest/ets/test/ImageKnifeOption.test.ets index ffd20b9..6a3acac 100644 --- a/entry/src/ohosTest/ets/test/ImageKnifeOption.test.ets +++ b/entry/src/ohosTest/ets/test/ImageKnifeOption.test.ets @@ -44,7 +44,7 @@ export default function ImageKnifeOptionTest() { imageWidth: 0, imageHeight: 0, } - let ImageKnifeOption: ImageKnifeOption = { + let imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r("app.media.rabbit"), onLoadListener: { onLoadFailed: (err) => { @@ -58,10 +58,10 @@ export default function ImageKnifeOptionTest() { return data; }, }, - } - if (ImageKnifeOption.onLoadListener && ImageKnifeOption.onLoadListener.onLoadSuccess && ImageKnifeOption.onLoadListener.onLoadFailed) { - ImageKnifeOption.onLoadListener.onLoadSuccess(a,imageData); - ImageKnifeOption.onLoadListener.onLoadFailed(a); + }) + if (imageKnifeOption.onLoadListener && imageKnifeOption.onLoadListener.onLoadSuccess && imageKnifeOption.onLoadListener.onLoadFailed) { + imageKnifeOption.onLoadListener.onLoadSuccess(a,imageData); + imageKnifeOption.onLoadListener.onLoadFailed(a); } expect(a).assertEqual(b); }); diff --git a/entry/src/ohosTest/ets/test/List.test.ets b/entry/src/ohosTest/ets/test/List.test.ets index 8c04309..9b109cc 100644 --- a/entry/src/ohosTest/ets/test/List.test.ets +++ b/entry/src/ohosTest/ets/test/List.test.ets @@ -18,6 +18,8 @@ import ImageKnifeOptionTest from './ImageKnifeOption.test'; import MemoryLruCacheTest from './MemoryLruCache.test'; import ImageKnifeTest from './ImageKnife.test'; import Transform from './transform.test'; +import SamplingTest from './SamplingTest.test'; +import imageFormatAndSize from './imageFormatAndSize.test' export default function testsuite() { MemoryLruCacheTest(); @@ -26,4 +28,6 @@ export default function testsuite() { ImageKnifeOptionTest(); ImageKnifeTest(); Transform(); + SamplingTest() + imageFormatAndSize(); } \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets b/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets index 98f2514..d0d7302 100644 --- a/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets +++ b/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets @@ -17,7 +17,7 @@ import image from '@ohos.multimedia.image'; import Constants from '../../../main/ets/common/Constants'; -import { MemoryLruCache } from '@ohos/imageknife/src/main/ets/utils/MemoryLruCache'; +import { MemoryLruCache } from '@ohos/imageknife/src/main/ets/cache/MemoryLruCache'; import { ImageKnifeData } from '@ohos/imageknife/src/main/ets/model/ImageKnifeData'; import { IEngineKey, ImageKnifeOption,ImageKnifeRequestSource } from '@ohos/imageknife'; import { DefaultEngineKey } from '@ohos/imageknife/src/main/ets/key/DefaultEngineKey'; @@ -122,9 +122,9 @@ export default function MemoryLruCacheTest() { it('memoryCacheEngineKey', 0, () => { let engineKey: IEngineKey = new DefaultEngineKey() - let imageKnifeOption: ImageKnifeOption = { + let imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc:"abc" - } + }) let imageKey = engineKey.generateMemoryKey(imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,imageKnifeOption) let imageAnimatorKey = engineKey.generateMemoryKey(imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,imageKnifeOption,true) expect(imageKey == imageAnimatorKey).assertFalse() diff --git a/entry/src/ohosTest/ets/test/SamplingTest.test.ets b/entry/src/ohosTest/ets/test/SamplingTest.test.ets new file mode 100644 index 0000000..074057d --- /dev/null +++ b/entry/src/ohosTest/ets/test/SamplingTest.test.ets @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import { image } from '@kit.ImageKit' +import { BusinessError } from '@kit.BasicServicesKit' +import { calculateScaleType, Downsampler } from '@ohos/imageknife/src/main/ets/downsampling/Downsampler' +import { DownsampleStrategy } from '@ohos/imageknife' + +export default function SamplingTest() { + describe('SamplingTest', () => { + // 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('NONE', 0, () => { + let reqSize: calculateScaleType = + new Downsampler().calculateScaling('jpg', 1024, 1024, 200, + 200, DownsampleStrategy.NONE,) + let req = (reqSize.targetWidth == 1024 && reqSize.targetHeight == 1024) + expect(req).assertEqual(true); + }) + it('AT_MOST', 1, () => { + let reqSize: calculateScaleType = + new Downsampler().calculateScaling('jpg', 1024, 1024, 200, + 200, DownsampleStrategy.AT_MOST) + let req = (reqSize.targetWidth < 1024 && reqSize.targetHeight < 1024) + expect(req).assertEqual(true); + }) + it('FIT_CENTER', 2, () => { + let reqSize: calculateScaleType = + new Downsampler().calculateScaling('jpg', 1024, 1024, 200, + 200, DownsampleStrategy.FIT_CENTER_MEMORY) + let req = (reqSize.targetWidth < 1024 && reqSize.targetHeight < 1024) + expect(req).assertEqual(true); + }) + it('CENTER_OUTSIDE', 3, () => { + let reqSize: calculateScaleType = + new Downsampler().calculateScaling('jpg', 1024, 1024, 200, + 200, DownsampleStrategy.CENTER_OUTSIDE_MEMORY) + let req = (reqSize.targetWidth < 1024 && reqSize.targetHeight < 1024) + expect(req).assertEqual(true); + }) + it('AT_LEAST', 4, () => { + let reqSize: calculateScaleType = + new Downsampler().calculateScaling('jpg', 1024, 1024, 200, + 200, DownsampleStrategy.AT_LEAST) + let req = (reqSize.targetWidth < 1024 && reqSize.targetHeight < 1024) + expect(req).assertEqual(true); + }) + + }) +} 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/customdatafetchclient.test.ets b/entry/src/ohosTest/ets/test/customdatafetchclient.test.ets new file mode 100644 index 0000000..38109e7 --- /dev/null +++ b/entry/src/ohosTest/ets/test/customdatafetchclient.test.ets @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import { DownloadClient } from '@ohos/imageknife/src/main/ets/components/imageknife/networkmanage/DownloadClient'; +import common from '@ohos.app.ability.common'; +import { GlobalContext } from '../testability/GlobalContext'; +import { CustomDataFetchClient, DataFetchResult, ImageKnifeGlobal, RequestOption } from '@ohos/imageknife'; + +const BASE_COUNT: number = 2000; + +export default function CustomDataFetchClientTest() { + describe('CustomDataFetchClientTest', () => { + // 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('TestIsLocalLoadSrc', 0, () => { + let path = 'invalid path'; + let client = new DownloadClient() + expect(client.isLocalLoadSrc(undefined, path)).assertFalse(); + let context: object | undefined = GlobalContext.getInstance().getObject("hapContext"); + if (context != undefined) { + let loadSrc1 = (context as common.UIAbilityContext).filesDir + 'a.jpg'; + let loadSrc2 = (context as common.UIAbilityContext).cacheDir + 'b.jpg'; + expect(client.isLocalLoadSrc(context, loadSrc1)).assertTrue(); + expect(client.isLocalLoadSrc(context, loadSrc2)).assertTrue(); + } + }) + it('TestLoadData', 1, async () => { + let client = new CustomDataFetchClient(); + let request = new RequestOption(); + request.loadSrc = $r('app.media.icon'); + let error = (await client.loadData(request) as DataFetchResult).error as String; + expect(error).assertEqual('CustomDataFetchClient request or loadSrc error.'); + }) + it('TestLoadData_customGetImage', 2, async () => { + let client = new CustomDataFetchClient(); + let request = new RequestOption(); + request.loadSrc = 'http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg'; + request.customGetImage = (context: Context, src: string) => { + // 这里是模拟的customGetImage逻辑 + return Promise.resolve(new DataFetchResult()); + } + console.log('LXH', 'TestLoadData 2 --1 customGetImage is undefined ?' + (request.customGetImage == undefined)); + let context: object | undefined = GlobalContext.getInstance().getObject("hapContext"); + let result = await client.loadData(request); + if (context != undefined) { + console.log('LXH', 'TestLoadData 2 --2'); + expect(typeof result) + .assertEqual(typeof (await request?.customGetImage(context as common.UIAbilityContext, request.loadSrc))); + } + }) + it('TestLoadData_combineArrayBuffers', 3, () => { + // 创建几个ArrayBuffer作为测试数据 + const arrayBuffer1 = new ArrayBuffer(4); + const uint8Array1 = new Uint8Array(arrayBuffer1); + uint8Array1[0] = 1; + uint8Array1[1] = 2; + uint8Array1[2] = 3; + uint8Array1[3] = 4; + + const arrayBuffer2 = new ArrayBuffer(2); + const uint8Array2 = new Uint8Array(arrayBuffer2); + uint8Array2[0] = 5; + uint8Array2[1] = 6; + let client = new CustomDataFetchClient(); + const combinedArrayBuffer = client.combineArrayBuffers([arrayBuffer1, arrayBuffer2]); + expect(combinedArrayBuffer.byteLength).assertEqual(6); + const combinedUint8Array = new Uint8Array(combinedArrayBuffer); + for (let i = 0; i < 4; i++) { + expect(combinedUint8Array[i]).assertEqual(uint8Array1[i]); + } + for (let i = 0; i < 2; i++) { + expect(combinedUint8Array[i + 4]).assertEqual(uint8Array2[i]); + } + }); + }) +} + diff --git a/entry/src/ohosTest/ets/test/imageFormatAndSize.test.ets b/entry/src/ohosTest/ets/test/imageFormatAndSize.test.ets new file mode 100644 index 0000000..fea2a53 --- /dev/null +++ b/entry/src/ohosTest/ets/test/imageFormatAndSize.test.ets @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; +import { + ImageKnifeOption, + ImageKnife, + ImageKnifeRequest, + ImageKnifeRequestSource, + CacheStrategy +} from "@ohos/imageknife" +import { common } from '@kit.AbilityKit'; + +export default function imageFormatAndSize() { + describe('imageFormatAndSize', () => { + // 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('getImageSizeInCache', 0, async () => { + let width = 0; + let height = 0; + let imageFormat: string = ""; + let url: string = + "https://contentcenter-drcn.dbankcdn.cn/pub_1/DevEcoSpace_1_900_9/ed/v3/KMO4D6D2QGuVOCLX4AhOFA/ef51xAaLQuK7BsnuD9abog.jpg" + let imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ + loadSrc: url, + }) + await new Promise((resolve, reject) => { + imageKnifeOption.onLoadListener = { + onLoadSuccess: (data, imageknifeData) => { + resolve("") + }, + onLoadFailed(err) { + reject(err) + } + } + let request = new ImageKnifeRequest( + imageKnifeOption, + imageKnifeOption.context !== undefined ? imageKnifeOption.context : getContext() as common.UIAbilityContext, + 0, + 0, + 0, + { + showPixelMap(version: number, pixelMap: PixelMap | string) { + } + } + ) + ImageKnife.getInstance().execute(request); + }) + let data = await ImageKnife.getInstance() + .getCacheImage(url, CacheStrategy.Memory); + if (data) { + width = data.imageWidth + height = data.imageHeight + imageFormat = data.type! + } + expect(width != 0).assertTrue(); + expect(height != 0).assertTrue(); + expect(imageFormat != "").assertTrue(); + }); + }); +} \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/imageknifeOption.test.ets b/entry/src/ohosTest/ets/test/imageknifeOption.test.ets new file mode 100644 index 0000000..6a3acac --- /dev/null +++ b/entry/src/ohosTest/ets/test/imageknifeOption.test.ets @@ -0,0 +1,69 @@ +/* + * 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 { ImageKnifeOption,ImageKnifeData } from "@ohos/imageknife" + +export default function ImageKnifeOptionTest() { + describe('ImageKnifeOptionTest',() => { + // 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('onLoadListener', 0, () => { + let a = 'abc'; + let b: string = ''; + let imageData:ImageKnifeData = { + source: "", + imageWidth: 0, + imageHeight: 0, + } + let imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ + loadSrc: $r("app.media.rabbit"), + onLoadListener: { + onLoadFailed: (err) => { + console.error("Load Failed Reason: " + err); + }, + onLoadSuccess: (data,imageknifeData) => { + if(typeof data == 'string') { + return b = data; + } + imageData = imageknifeData + return data; + }, + }, + }) + if (imageKnifeOption.onLoadListener && imageKnifeOption.onLoadListener.onLoadSuccess && imageKnifeOption.onLoadListener.onLoadFailed) { + imageKnifeOption.onLoadListener.onLoadSuccess(a,imageData); + imageKnifeOption.onLoadListener.onLoadFailed(a); + } + expect(a).assertEqual(b); + }); + }); +} \ No newline at end of file diff --git a/entry/src/ohosTest/module.json5 b/entry/src/ohosTest/module.json5 index d0dc5d5..4fc9701 100644 --- a/entry/src/ohosTest/module.json5 +++ b/entry/src/ohosTest/module.json5 @@ -5,9 +5,8 @@ "description": "$string:module_test_desc", "mainElement": "TestAbility", "deviceTypes": [ - "phone", - "tablet", - "2in1" + "default", + "tablet" ], "deliveryWithInstall": true, "installationFree": false, diff --git a/gpu_transform/CHANGELOG.md b/gpu_transform/CHANGELOG.md index 3189513..8898f8d 100644 --- a/gpu_transform/CHANGELOG.md +++ b/gpu_transform/CHANGELOG.md @@ -1,7 +1,11 @@ -## 1.0.3 +## 1.0.4 - 修改门禁编译问题 修改点如下 +- 修改src/main/cpp/boundscheck/CMakeLists.txt文件中的内容 - 修改src/main/cpp/util/DebugLog.h文件中hilog的大小写 +## 1.0.3 +- 安全编译开启Strip和Ftrapv + ## 1.0.2 - 支持x86编译 diff --git a/gpu_transform/build-profile.json5 b/gpu_transform/build-profile.json5 index c3f8f1b..f4b0a30 100644 --- a/gpu_transform/build-profile.json5 +++ b/gpu_transform/build-profile.json5 @@ -3,7 +3,7 @@ "buildOption": { "externalNativeOptions": { "path": "./src/main/cpp/CMakeLists.txt", - "arguments": "", + "arguments": " -DCMAKE_BUILD_TYPE=Release", "abiFilters": [ "armeabi-v7a", "arm64-v8a", diff --git a/gpu_transform/oh-package.json5 b/gpu_transform/oh-package.json5 index 1eb6bf3..1526ae0 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.3", + "version": "1.0.4", "tags": [ "Tool" ], diff --git a/gpu_transform/src/main/cpp/CMakeLists.txt b/gpu_transform/src/main/cpp/CMakeLists.txt index 6407f87..0751911 100644 --- a/gpu_transform/src/main/cpp/CMakeLists.txt +++ b/gpu_transform/src/main/cpp/CMakeLists.txt @@ -11,8 +11,11 @@ include_directories(${NATIVERENDER_ROOT_PATH} ${NATIVERENDER_ROOT_PATH}/common ${NATIVERENDER_ROOT_PATH}/render ${NATIVERENDER_ROOT_PATH}/constant + ${NATIVERENDER_ROOT_PATH}/boundscheck ) +add_subdirectory(boundscheck) + add_library(nativeGpu SHARED ${NATIVERENDER_ROOT_PATH}/napi/napi_init.cpp ${NATIVERENDER_ROOT_PATH}/render/EGLRender.cpp @@ -30,4 +33,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 boundscheck ${hilog-lib} ${EGL-lib} ${GLES-lib} libace_napi.z.so -s -ftrapv) \ No newline at end of file diff --git a/gpu_transform/src/main/cpp/boundscheck/CMakeLists.txt b/gpu_transform/src/main/cpp/boundscheck/CMakeLists.txt new file mode 100644 index 0000000..c1bb6a6 --- /dev/null +++ b/gpu_transform/src/main/cpp/boundscheck/CMakeLists.txt @@ -0,0 +1,27 @@ +# the minimum version of CMake. +cmake_minimum_required(VERSION 3.4.1) + +set(can_use_assembler TRUE) +enable_language(ASM) +IF("${OHOS_ARCH}" STREQUAL "arm64-v8a") + SET(ASM_OPTIONS "-x assembler-with-cpp") + SET(CMAKE_ASM_FLAGS "${CFLAGS} ${ASM_OPTIONS} -march=armv8+crypto -D__OHOS__") +ENDIF() + +project(boundscheck) + +set(TAGET_BOUNDSCHECK_SRC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/third_party_bounds_checking_function/src) + +add_library(boundscheck + STATIC + ${TAGET_BOUNDSCHECK_SRC_PATH}/memcpy_s.c + ${TAGET_BOUNDSCHECK_SRC_PATH}/memset_s.c + ${TAGET_BOUNDSCHECK_SRC_PATH}/securecutil.c) + +target_precompile_headers(boundscheck PUBLIC ${CMAKE_SYSROOT}/usr/include/stdint.h) + +include_directories(${TAGET_BOUNDSCHECK_SRC_PATH} + ${CMAKE_CURRENT_SOURCE_DIR}/third_party_bounds_checking_function/include + ) + +target_link_libraries(boundscheck) diff --git a/gpu_transform/src/main/cpp/boundscheck/third_party_bounds_checking_function b/gpu_transform/src/main/cpp/boundscheck/third_party_bounds_checking_function new file mode 160000 index 0000000..a45b3ac --- /dev/null +++ b/gpu_transform/src/main/cpp/boundscheck/third_party_bounds_checking_function @@ -0,0 +1 @@ +Subproject commit a45b3aceed2c0138babc951850445d1cd010ce48 diff --git a/gpu_transform/src/main/cpp/render/EGLRender.cpp b/gpu_transform/src/main/cpp/render/EGLRender.cpp index 0a051de..f0ef9b2 100644 --- a/gpu_transform/src/main/cpp/render/EGLRender.cpp +++ b/gpu_transform/src/main/cpp/render/EGLRender.cpp @@ -535,7 +535,10 @@ void EGLRender::SetImageData(uint8_t *pData, int width, int height) m_RenderImage.height = height; m_RenderImage.format = IMAGE_FORMAT_RGBA; NativeImageUtil::AllocNativeImage(&m_RenderImage); - memcpy(m_RenderImage.ppPlane[0], pData, width * height * DEFAULT_FOUR); + if (memcpy_s(m_RenderImage.ppPlane[0], + width * height * DEFAULT_FOUR, pData, width * height * DEFAULT_FOUR) != EOK) { + return; + } glBindTexture(GL_TEXTURE_2D, m_ImageTextureId); glTexImage2D(GL_TEXTURE_2D, diff --git a/gpu_transform/src/main/cpp/util/NapiUtil.cpp b/gpu_transform/src/main/cpp/util/NapiUtil.cpp index eba879f..271e7b2 100644 --- a/gpu_transform/src/main/cpp/util/NapiUtil.cpp +++ b/gpu_transform/src/main/cpp/util/NapiUtil.cpp @@ -26,6 +26,7 @@ #include #include #include "DebugLog.h" +#include "third_party_bounds_checking_function/include/securec.h" const int32_t MAX_STR_LENGTH = 1024; @@ -41,7 +42,7 @@ void NapiUtil::JsValueToString(const napi_env &env, const napi_value &value, con LOGI("%s nullptr js object to string malloc failed", __func__); return; } - (void) memset(buf.get(), 0, bufLen); + (void) memset_s(buf.get(), bufLen, 0, bufLen); size_t result = 0; napi_get_value_string_utf8(env, value, buf.get(), bufLen, &result); target = buf.get(); diff --git a/gpu_transform/src/main/cpp/util/NativeImage.h b/gpu_transform/src/main/cpp/util/NativeImage.h index d79018d..c97c0f1 100644 --- a/gpu_transform/src/main/cpp/util/NativeImage.h +++ b/gpu_transform/src/main/cpp/util/NativeImage.h @@ -30,6 +30,7 @@ #include #include "DebugLog.h" #include "constant/constant_shape.h" +#include "third_party_bounds_checking_function/include/securec.h" #define IMAGE_FORMAT_RGBA 0x01 #define IMAGE_FORMAT_NV21 0x02 @@ -48,7 +49,7 @@ #define IMAGE_FORMAT_GRAY_EXT "GRAY" #define IMAGE_FORMAT_I444_EXT "I444" #define IMAGE_FORMAT_P010_EXT "P010" // 16bit NV21 - +#define EOK 0 struct NativeImage { int width; @@ -140,7 +141,9 @@ public: if (napi_create_arraybuffer(env, srcLen, &nativePtr, res) != napi_ok || nativePtr == nullptr) { return false; } - memcpy(nativePtr, src, srcLen); + if (memcpy_s(nativePtr, srcLen, src, srcLen) != EOK) { + return false; + } return true; } @@ -149,10 +152,17 @@ public: int totalLength = width * height * DEFAULT_FOUR; int oneLineLength = width * DEFAULT_FOUR; uint8_t* tmp = (uint8_t*)malloc(totalLength); - memcpy(tmp, *buf, totalLength); - memset(*buf, DEFAULT_ZERO, sizeof(uint8_t)*totalLength); + if (memcpy_s(tmp, totalLength, *buf, totalLength) != EOK) { + return; + } + if (memset_s(*buf, sizeof(uint8_t)*totalLength, DEFAULT_ZERO, sizeof(uint8_t)*totalLength) != EOK) { + return; + } for (int i = 0; i < height; i++) { - memcpy(*buf + oneLineLength * i, tmp + totalLength - oneLineLength * (i+1), oneLineLength); + if (memcpy_s(*buf + oneLineLength * i, oneLineLength, + tmp + totalLength - oneLineLength * (i+1), oneLineLength) != EOK) { + break; + } } free(tmp); } diff --git a/hvigor/hvigor-config.json5 b/hvigor/hvigor-config.json5 index 8c56bf6..eb0d70f 100644 --- a/hvigor/hvigor-config.json5 +++ b/hvigor/hvigor-config.json5 @@ -1,6 +1,7 @@ { - "modelVersion": "5.0.0", + "hvigorVersion": "4.0.2", "dependencies": { + "@ohos/hvigor-ohos-plugin": "4.0.2" }, "execution": { // "analyze": "default", /* Define the build analyze mode. Value: [ "default" | "verbose" | false ]. Default: "default" */ @@ -18,4 +19,4 @@ "nodeOptions": { // "maxOldSpaceSize": 4096 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process */ } -} \ No newline at end of file +} diff --git a/hvigor/hvigor-wrapper.js b/hvigor/hvigor-wrapper.js new file mode 100644 index 0000000..26073b8 --- /dev/null +++ b/hvigor/hvigor-wrapper.js @@ -0,0 +1 @@ +"use strict";var u=require("path"),D=require("os"),e=require("fs"),t=require("crypto"),r=require("child_process"),n="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},i={},C={},E=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(C,"__esModule",{value:!0}),C.maxPathLength=C.isMac=C.isLinux=C.isWindows=void 0;const F=E(D),A="Windows_NT",o="Darwin";function a(){return F.default.type()===A}function c(){return F.default.type()===o}C.isWindows=a,C.isLinux=function(){return"Linux"===F.default.type()},C.isMac=c,C.maxPathLength=function(){return c()?1016:a()?259:4095},function(e){var t=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),r=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),i=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&t(D,u,e);return r(D,u),D};Object.defineProperty(e,"__esModule",{value:!0}),e.ENABLE_SIGN_TASK_KEY=e.HVIGOR_CACHE_DIR_KEY=e.WORK_SPACE=e.HVIGOR_PROJECT_WRAPPER_HOME=e.HVIGOR_PROJECT_ROOT_DIR=e.HVIGOR_PROJECT_CACHES_HOME=e.HVIGOR_PNPM_STORE_PATH=e.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=e.PROJECT_CACHES=e.HVIGOR_WRAPPER_TOOLS_HOME=e.HVIGOR_USER_HOME=e.DEFAULT_PACKAGE_JSON=e.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME=e.PNPM=e.HVIGOR=e.NPM_TOOL=e.PNPM_TOOL=e.HVIGOR_ENGINE_PACKAGE_NAME=void 0;const E=i(D),F=i(u),A=C;e.HVIGOR_ENGINE_PACKAGE_NAME="@ohos/hvigor",e.PNPM_TOOL=(0,A.isWindows)()?"pnpm.cmd":"pnpm",e.NPM_TOOL=(0,A.isWindows)()?"npm.cmd":"npm",e.HVIGOR="hvigor",e.PNPM="pnpm",e.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME="hvigor-config.json5",e.DEFAULT_PACKAGE_JSON="package.json",e.HVIGOR_USER_HOME=F.resolve(E.homedir(),".hvigor"),e.HVIGOR_WRAPPER_TOOLS_HOME=F.resolve(e.HVIGOR_USER_HOME,"wrapper","tools"),e.PROJECT_CACHES="project_caches",e.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=F.resolve(e.HVIGOR_WRAPPER_TOOLS_HOME,"node_modules",".bin",e.PNPM_TOOL),e.HVIGOR_PNPM_STORE_PATH=F.resolve(e.HVIGOR_USER_HOME,"caches"),e.HVIGOR_PROJECT_CACHES_HOME=F.resolve(e.HVIGOR_USER_HOME,e.PROJECT_CACHES),e.HVIGOR_PROJECT_ROOT_DIR=process.cwd(),e.HVIGOR_PROJECT_WRAPPER_HOME=F.resolve(e.HVIGOR_PROJECT_ROOT_DIR,e.HVIGOR),e.WORK_SPACE="workspace",e.HVIGOR_CACHE_DIR_KEY="hvigor.cacheDir",e.ENABLE_SIGN_TASK_KEY="enableSignTask"}(i);var s={},l={};Object.defineProperty(l,"__esModule",{value:!0}),l.logInfoPrintConsole=l.logErrorAndExit=void 0,l.logErrorAndExit=function(u){u instanceof Error?console.error(u.message):console.error(u),process.exit(-1)},l.logInfoPrintConsole=function(u){console.log(u)};var B=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),d=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),f=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&B(D,u,e);return d(D,u),D};Object.defineProperty(s,"__esModule",{value:!0});var _=s.executeBuild=void 0;const O=f(e),p=f(u),h=l;_=s.executeBuild=function(u){const D=p.resolve(u,"node_modules","@ohos","hvigor","bin","hvigor.js");try{const u=O.realpathSync(D);require(u)}catch(e){(0,h.logErrorAndExit)(`Error: ENOENT: no such file ${D},delete ${u} and retry.`)}};var P={},v={};!function(u){var D=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(u,"__esModule",{value:!0}),u.hashFile=u.hash=u.createHash=void 0;const r=D(t),i=D(e);u.createHash=(u="MD5")=>r.default.createHash(u);u.hash=(D,e)=>(0,u.createHash)(e).update(D).digest("hex");u.hashFile=(D,e)=>{if(i.default.existsSync(D))return(0,u.hash)(i.default.readFileSync(D,"utf-8"),e)}}(v);var g={},m={},R={};Object.defineProperty(R,"__esModule",{value:!0}),R.Unicode=void 0;class I{}R.Unicode=I,I.SPACE_SEPARATOR=/[\u1680\u2000-\u200A\u202F\u205F\u3000]/,I.ID_START=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE83\uDE86-\uDE89\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]/,I.ID_CONTINUE=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u09FC\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9-\u0AFF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D00-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF9\u1D00-\u1DF9\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE3E\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC00-\uDC4A\uDC50-\uDC59\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDE00-\uDE3E\uDE47\uDE50-\uDE83\uDE86-\uDE99\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC36\uDC38-\uDC40\uDC50-\uDC59\uDC72-\uDC8F\uDC92-\uDCA7\uDCA9-\uDCB6\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD36\uDD3A\uDD3C\uDD3D\uDD3F-\uDD47\uDD50-\uDD59]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6\uDD00-\uDD4A\uDD50-\uDD59]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/,Object.defineProperty(m,"__esModule",{value:!0}),m.JudgeUtil=void 0;const y=R;m.JudgeUtil=class{static isIgnoreChar(u){return"string"==typeof u&&("\t"===u||"\v"===u||"\f"===u||" "===u||" "===u||"\ufeff"===u||"\n"===u||"\r"===u||"\u2028"===u||"\u2029"===u)}static isSpaceSeparator(u){return"string"==typeof u&&y.Unicode.SPACE_SEPARATOR.test(u)}static isIdStartChar(u){return"string"==typeof u&&(u>="a"&&u<="z"||u>="A"&&u<="Z"||"$"===u||"_"===u||y.Unicode.ID_START.test(u))}static isIdContinueChar(u){return"string"==typeof u&&(u>="a"&&u<="z"||u>="A"&&u<="Z"||u>="0"&&u<="9"||"$"===u||"_"===u||"‌"===u||"‍"===u||y.Unicode.ID_CONTINUE.test(u))}static isDigitWithoutZero(u){return/[1-9]/.test(u)}static isDigit(u){return"string"==typeof u&&/[0-9]/.test(u)}static isHexDigit(u){return"string"==typeof u&&/[0-9A-Fa-f]/.test(u)}};var N=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(g,"__esModule",{value:!0}),g.parseJsonText=g.parseJsonFile=void 0;const S=N(e),b=N(D),w=N(u),H=m;var x;!function(u){u[u.Char=0]="Char",u[u.EOF=1]="EOF",u[u.Identifier=2]="Identifier"}(x||(x={}));let M,T,G,V,j,J,U="start",L=[],W=0,$=1,K=0,k=!1,z="default",q="'",Z=1;function Y(u,D=!1){T=String(u),U="start",L=[],W=0,$=1,K=0,V=void 0,k=D;do{M=X(),nu[U]()}while("eof"!==M.type);return V}function X(){for(z="default",j="",q="'",Z=1;;){J=Q();const u=Du[z]();if(u)return u}}function Q(){if(T[W])return String.fromCodePoint(T.codePointAt(W))}function uu(){const u=Q();return"\n"===u?($++,K=0):u?K+=u.length:K++,u&&(W+=u.length),u}g.parseJsonFile=function(u,D=!1,e="utf-8"){const t=S.default.readFileSync(w.default.resolve(u),{encoding:e});try{return Y(t,D)}catch(D){if(D instanceof SyntaxError){const e=D.message.split("at");if(2===e.length)throw new Error(`${e[0].trim()}${b.default.EOL}\t at ${u}:${e[1].trim()}`)}throw new Error(`${u} is not in valid JSON/JSON5 format.`)}},g.parseJsonText=Y;const Du={default(){switch(J){case"/":return uu(),void(z="comment");case void 0:return uu(),eu("eof")}if(!H.JudgeUtil.isIgnoreChar(J)&&!H.JudgeUtil.isSpaceSeparator(J))return Du[U]();uu()},start(){z="value"},beforePropertyName(){switch(J){case"$":case"_":return j=uu(),void(z="identifierName");case"\\":return uu(),void(z="identifierNameStartEscape");case"}":return eu("punctuator",uu());case'"':case"'":return q=J,uu(),void(z="string")}if(H.JudgeUtil.isIdStartChar(J))return j+=uu(),void(z="identifierName");throw Fu(x.Char,uu())},afterPropertyName(){if(":"===J)return eu("punctuator",uu());throw Fu(x.Char,uu())},beforePropertyValue(){z="value"},afterPropertyValue(){switch(J){case",":case"}":return eu("punctuator",uu())}throw Fu(x.Char,uu())},beforeArrayValue(){if("]"===J)return eu("punctuator",uu());z="value"},afterArrayValue(){switch(J){case",":case"]":return eu("punctuator",uu())}throw Fu(x.Char,uu())},end(){throw Fu(x.Char,uu())},comment(){switch(J){case"*":return uu(),void(z="multiLineComment");case"/":return uu(),void(z="singleLineComment")}throw Fu(x.Char,uu())},multiLineComment(){switch(J){case"*":return uu(),void(z="multiLineCommentAsterisk");case void 0:throw Fu(x.Char,uu())}uu()},multiLineCommentAsterisk(){switch(J){case"*":return void uu();case"/":return uu(),void(z="default");case void 0:throw Fu(x.Char,uu())}uu(),z="multiLineComment"},singleLineComment(){switch(J){case"\n":case"\r":case"\u2028":case"\u2029":return uu(),void(z="default");case void 0:return uu(),eu("eof")}uu()},value(){switch(J){case"{":case"[":return eu("punctuator",uu());case"n":return uu(),tu("ull"),eu("null",null);case"t":return uu(),tu("rue"),eu("boolean",!0);case"f":return uu(),tu("alse"),eu("boolean",!1);case"-":case"+":return"-"===uu()&&(Z=-1),void(z="numerical");case".":case"0":case"I":case"N":return void(z="numerical");case'"':case"'":return q=J,uu(),j="",void(z="string")}if(void 0===J||!H.JudgeUtil.isDigitWithoutZero(J))throw Fu(x.Char,uu());z="numerical"},numerical(){switch(J){case".":return j=uu(),void(z="decimalPointLeading");case"0":return j=uu(),void(z="zero");case"I":return uu(),tu("nfinity"),eu("numeric",Z*(1/0));case"N":return uu(),tu("aN"),eu("numeric",NaN)}if(void 0!==J&&H.JudgeUtil.isDigitWithoutZero(J))return j=uu(),void(z="decimalInteger");throw Fu(x.Char,uu())},zero(){switch(J){case".":case"e":case"E":return void(z="decimal");case"x":case"X":return j+=uu(),void(z="hexadecimal")}return eu("numeric",0)},decimalInteger(){switch(J){case".":case"e":case"E":return void(z="decimal")}if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},decimal(){switch(J){case".":j+=uu(),z="decimalFraction";break;case"e":case"E":j+=uu(),z="decimalExponent"}},decimalPointLeading(){if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalFraction");throw Fu(x.Char,uu())},decimalFraction(){switch(J){case"e":case"E":return j+=uu(),void(z="decimalExponent")}if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},decimalExponent(){switch(J){case"+":case"-":return j+=uu(),void(z="decimalExponentSign")}if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalExponentInteger");throw Fu(x.Char,uu())},decimalExponentSign(){if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalExponentInteger");throw Fu(x.Char,uu())},decimalExponentInteger(){if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},hexadecimal(){if(H.JudgeUtil.isHexDigit(J))return j+=uu(),void(z="hexadecimalInteger");throw Fu(x.Char,uu())},hexadecimalInteger(){if(!H.JudgeUtil.isHexDigit(J))return eu("numeric",Z*Number(j));j+=uu()},identifierNameStartEscape(){if("u"!==J)throw Fu(x.Char,uu());uu();const u=ru();switch(u){case"$":case"_":break;default:if(!H.JudgeUtil.isIdStartChar(u))throw Fu(x.Identifier)}j+=u,z="identifierName"},identifierName(){switch(J){case"$":case"_":case"‌":case"‍":return void(j+=uu());case"\\":return uu(),void(z="identifierNameEscape")}if(!H.JudgeUtil.isIdContinueChar(J))return eu("identifier",j);j+=uu()},identifierNameEscape(){if("u"!==J)throw Fu(x.Char,uu());uu();const u=ru();switch(u){case"$":case"_":case"‌":case"‍":break;default:if(!H.JudgeUtil.isIdContinueChar(u))throw Fu(x.Identifier)}j+=u,z="identifierName"},string(){switch(J){case"\\":return uu(),void(j+=function(){const u=Q(),D=function(){switch(Q()){case"b":return uu(),"\b";case"f":return uu(),"\f";case"n":return uu(),"\n";case"r":return uu(),"\r";case"t":return uu(),"\t";case"v":return uu(),"\v"}return}();if(D)return D;switch(u){case"0":if(uu(),H.JudgeUtil.isDigit(Q()))throw Fu(x.Char,uu());return"\0";case"x":return uu(),function(){let u="",D=Q();if(!H.JudgeUtil.isHexDigit(D))throw Fu(x.Char,uu());if(u+=uu(),D=Q(),!H.JudgeUtil.isHexDigit(D))throw Fu(x.Char,uu());return u+=uu(),String.fromCodePoint(parseInt(u,16))}();case"u":return uu(),ru();case"\n":case"\u2028":case"\u2029":return uu(),"";case"\r":return uu(),"\n"===Q()&&uu(),""}if(void 0===u||H.JudgeUtil.isDigitWithoutZero(u))throw Fu(x.Char,uu());return uu()}());case'"':case"'":if(J===q){const u=eu("string",j);return uu(),u}return void(j+=uu());case"\n":case"\r":case void 0:throw Fu(x.Char,uu());case"\u2028":case"\u2029":!function(u){console.warn(`JSON5: '${Eu(u)}' in strings is not valid ECMAScript; consider escaping.`)}(J)}j+=uu()}};function eu(u,D){return{type:u,value:D,line:$,column:K}}function tu(u){for(const D of u){if(Q()!==D)throw Fu(x.Char,uu());uu()}}function ru(){let u="",D=4;for(;D-- >0;){const D=Q();if(!H.JudgeUtil.isHexDigit(D))throw Fu(x.Char,uu());u+=uu()}return String.fromCodePoint(parseInt(u,16))}const nu={start(){if("eof"===M.type)throw Fu(x.EOF);iu()},beforePropertyName(){switch(M.type){case"identifier":case"string":return G=M.value,void(U="afterPropertyName");case"punctuator":return void Cu();case"eof":throw Fu(x.EOF)}},afterPropertyName(){if("eof"===M.type)throw Fu(x.EOF);U="beforePropertyValue"},beforePropertyValue(){if("eof"===M.type)throw Fu(x.EOF);iu()},afterPropertyValue(){if("eof"===M.type)throw Fu(x.EOF);switch(M.value){case",":return void(U="beforePropertyName");case"}":Cu()}},beforeArrayValue(){if("eof"===M.type)throw Fu(x.EOF);"punctuator"!==M.type||"]"!==M.value?iu():Cu()},afterArrayValue(){if("eof"===M.type)throw Fu(x.EOF);switch(M.value){case",":return void(U="beforeArrayValue");case"]":Cu()}},end(){}};function iu(){const u=function(){let u;switch(M.type){case"punctuator":switch(M.value){case"{":u={};break;case"[":u=[]}break;case"null":case"boolean":case"numeric":case"string":u=M.value}return u}();if(k&&"object"==typeof u&&(u._line=$,u._column=K),void 0===V)V=u;else{const D=L[L.length-1];Array.isArray(D)?k&&"object"!=typeof u?D.push({value:u,_line:$,_column:K}):D.push(u):D[G]=k&&"object"!=typeof u?{value:u,_line:$,_column:K}:u}!function(u){if(u&&"object"==typeof u)L.push(u),U=Array.isArray(u)?"beforeArrayValue":"beforePropertyName";else{const u=L[L.length-1];U=u?Array.isArray(u)?"afterArrayValue":"afterPropertyValue":"end"}}(u)}function Cu(){L.pop();const u=L[L.length-1];U=u?Array.isArray(u)?"afterArrayValue":"afterPropertyValue":"end"}function Eu(u){const D={"'":"\\'",'"':'\\"',"\\":"\\\\","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\v":"\\v","\0":"\\0","\u2028":"\\u2028","\u2029":"\\u2029"};if(D[u])return D[u];if(u<" "){const D=u.charCodeAt(0).toString(16);return`\\x${`00${D}`.substring(D.length)}`}return u}function Fu(u,D){let e="";switch(u){case x.Char:e=void 0===D?`JSON5: invalid end of input at ${$}:${K}`:`JSON5: invalid character '${Eu(D)}' at ${$}:${K}`;break;case x.EOF:e=`JSON5: invalid end of input at ${$}:${K}`;break;case x.Identifier:K-=5,e=`JSON5: invalid identifier character at ${$}:${K}`}const t=new Au(e);return t.lineNumber=$,t.columnNumber=K,t}class Au extends SyntaxError{}var ou={},au=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),cu=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),su=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&au(D,u,e);return cu(D,u),D},lu=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(ou,"__esModule",{value:!0}),ou.isFileExists=ou.offlinePluginConversion=ou.executeCommand=ou.getNpmPath=ou.hasNpmPackInPaths=void 0;const Bu=r,du=lu(e),fu=su(u),_u=i,Ou=l;ou.hasNpmPackInPaths=function(u,D){try{return require.resolve(u,{paths:[...D]}),!0}catch(u){return!1}},ou.getNpmPath=function(){const u=process.execPath;return fu.join(fu.dirname(u),_u.NPM_TOOL)},ou.executeCommand=function(u,D,e){0!==(0,Bu.spawnSync)(u,D,e).status&&(0,Ou.logErrorAndExit)(`Error: ${u} ${D} execute failed.See above for details.`)},ou.offlinePluginConversion=function(u,D){return D.startsWith("file:")||D.endsWith(".tgz")?fu.resolve(u,_u.HVIGOR,D.replace("file:","")):D},ou.isFileExists=function(u){return du.default.existsSync(u)&&du.default.statSync(u).isFile()};var pu=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),hu=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),Pu=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&pu(D,u,e);return hu(D,u),D},vu=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(P,"__esModule",{value:!0});var gu=P.initProjectWorkSpace=void 0;const mu=Pu(e),Ru=vu(D),Iu=Pu(u),yu=i,Nu=v,Su=g,bu=l,wu=ou;let Hu,xu,Mu;function Tu(u,D,e){return void 0!==e.dependencies&&(0,wu.offlinePluginConversion)(yu.HVIGOR_PROJECT_ROOT_DIR,D.dependencies[u])===Iu.normalize(e.dependencies[u])}gu=P.initProjectWorkSpace=function(){if(Hu=function(){const u=Iu.resolve(yu.HVIGOR_PROJECT_WRAPPER_HOME,yu.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME);mu.existsSync(u)||(0,bu.logErrorAndExit)(`Error: Hvigor config file ${u} does not exist.`);return(0,Su.parseJsonFile)(u)}(),Mu=function(u){let D;D=function(u){const D=u.hvigorVersion;if(D.startsWith("file:")||D.endsWith(".tgz"))return!1;const e=u.dependencies,t=Object.getOwnPropertyNames(e);for(const u of t){const D=e[u];if(D.startsWith("file:")||D.endsWith(".tgz"))return!1}if(1===t.length&&"@ohos/hvigor-ohos-plugin"===t[0])return D>"2.5.0";return!1}(u)?function(u){let D=`${yu.HVIGOR_ENGINE_PACKAGE_NAME}@${u.hvigorVersion}`;const e=u.dependencies;if(e){Object.getOwnPropertyNames(e).sort().forEach((u=>{D+=`,${u}@${e[u]}`}))}return(0,Nu.hash)(D)}(u):(0,Nu.hash)(process.cwd());return Iu.resolve(Ru.default.homedir(),".hvigor","project_caches",D)}(Hu),xu=function(){const u=Iu.resolve(Mu,yu.WORK_SPACE,yu.DEFAULT_PACKAGE_JSON);return mu.existsSync(u)?(0,Su.parseJsonFile)(u):{dependencies:{}}}(),function(){const u=Iu.resolve(yu.HVIGOR_USER_HOME,yu.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME);if(mu.existsSync(u))(0,Su.parseJsonFile)(u)}(),!(0,wu.hasNpmPackInPaths)(yu.HVIGOR_ENGINE_PACKAGE_NAME,[Iu.join(Mu,yu.WORK_SPACE)])||(0,wu.offlinePluginConversion)(yu.HVIGOR_PROJECT_ROOT_DIR,Hu.hvigorVersion)!==xu.dependencies[yu.HVIGOR_ENGINE_PACKAGE_NAME]||!function(){function u(u){const D=null==u?void 0:u.dependencies;return void 0===D?0:Object.getOwnPropertyNames(D).length}const D=u(Hu),e=u(xu);if(D+1!==e)return!1;for(const u in null==Hu?void 0:Hu.dependencies)if(!(0,wu.hasNpmPackInPaths)(u,[Iu.join(Mu,yu.WORK_SPACE)])||!Tu(u,Hu,xu))return!1;return!0}())try{!function(){(0,bu.logInfoPrintConsole)("Hvigor installing...");for(const u in Hu.dependencies)Hu.dependencies[u]&&(Hu.dependencies[u]=(0,wu.offlinePluginConversion)(yu.HVIGOR_PROJECT_ROOT_DIR,Hu.dependencies[u]));const u={dependencies:{...Hu.dependencies}};u.dependencies[yu.HVIGOR_ENGINE_PACKAGE_NAME]=(0,wu.offlinePluginConversion)(yu.HVIGOR_PROJECT_ROOT_DIR,Hu.hvigorVersion);const D=Iu.join(Mu,yu.WORK_SPACE);try{mu.mkdirSync(D,{recursive:!0});const e=Iu.resolve(D,yu.DEFAULT_PACKAGE_JSON);mu.writeFileSync(e,JSON.stringify(u))}catch(u){(0,bu.logErrorAndExit)(u)}(function(){const u=["config","set","store-dir",yu.HVIGOR_PNPM_STORE_PATH],D={cwd:Iu.join(Mu,yu.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,wu.executeCommand)(yu.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,u,D)})(),function(){const u=["install"],D={cwd:Iu.join(Mu,yu.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,wu.executeCommand)(yu.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,u,D)}(),(0,bu.logInfoPrintConsole)("Hvigor install success.")}()}catch(u){!function(){const u=Iu.join(Mu,yu.WORK_SPACE);if((0,bu.logInfoPrintConsole)("Hvigor cleaning..."),!mu.existsSync(u))return;const D=mu.readdirSync(u);if(!D||0===D.length)return;const e=Iu.resolve(Mu,"node_modules","@ohos","hvigor","bin","hvigor.js");mu.existsSync(e)&&(0,wu.executeCommand)(process.argv[0],[e,"--stop-daemon"],{});try{D.forEach((D=>{mu.rmSync(Iu.resolve(u,D),{recursive:!0})}))}catch(D){(0,bu.logErrorAndExit)(`The hvigor build tool cannot be installed. Please manually clear the workspace directory and synchronize the project again.\n\n Workspace Path: ${u}.`)}}()}return Mu};var Gu={};!function(t){var C=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),E=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),F=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&C(D,u,e);return E(D,u),D},A=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(t,"__esModule",{value:!0}),t.executeInstallPnpm=t.isPnpmInstalled=t.environmentHandler=t.checkNpmConifg=t.PNPM_VERSION=void 0;const o=r,a=F(e),c=A(D),s=F(u),B=i,d=l,f=ou;t.PNPM_VERSION="7.30.0",t.checkNpmConifg=function(){const u=s.resolve(B.HVIGOR_PROJECT_ROOT_DIR,".npmrc"),D=s.resolve(c.default.homedir(),".npmrc");if((0,f.isFileExists)(u)||(0,f.isFileExists)(D))return;const e=(0,f.getNpmPath)(),t=(0,o.spawnSync)(e,["config","get","prefix"],{cwd:B.HVIGOR_PROJECT_ROOT_DIR});if(0!==t.status||!t.stdout)return void(0,d.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.");const r=s.resolve(`${t.stdout}`.replace(/[\r\n]/gi,""),".npmrc");(0,f.isFileExists)(r)||(0,d.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.")},t.environmentHandler=function(){process.env["npm_config_update-notifier"]="false"},t.isPnpmInstalled=function(){return!!a.existsSync(B.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH)&&(0,f.hasNpmPackInPaths)("pnpm",[B.HVIGOR_WRAPPER_TOOLS_HOME])},t.executeInstallPnpm=function(){(0,d.logInfoPrintConsole)(`Installing pnpm@${t.PNPM_VERSION}...`);const u=(0,f.getNpmPath)();!function(){const u=s.resolve(B.HVIGOR_WRAPPER_TOOLS_HOME,B.DEFAULT_PACKAGE_JSON);try{a.existsSync(B.HVIGOR_WRAPPER_TOOLS_HOME)||a.mkdirSync(B.HVIGOR_WRAPPER_TOOLS_HOME,{recursive:!0});const D={dependencies:{}};D.dependencies[B.PNPM]=t.PNPM_VERSION,a.writeFileSync(u,JSON.stringify(D))}catch(D){(0,d.logErrorAndExit)(`Error: EPERM: operation not permitted,create ${u} failed.`)}}(),(0,f.executeCommand)(u,["install","pnpm"],{cwd:B.HVIGOR_WRAPPER_TOOLS_HOME,stdio:["inherit","inherit","inherit"],env:process.env}),(0,d.logInfoPrintConsole)("Pnpm install success.")}}(Gu),function(){Gu.checkNpmConifg(),Gu.environmentHandler(),Gu.isPnpmInstalled()||Gu.executeInstallPnpm();const D=gu();_(u.join(D,i.WORK_SPACE))}(); \ No newline at end of file diff --git a/hvigorw b/hvigorw new file mode 100644 index 0000000..54aadd2 --- /dev/null +++ b/hvigorw @@ -0,0 +1,48 @@ +#!/bin/bash + +# ---------------------------------------------------------------------------- +# Hvigor startup script, version 1.0.0 +# +# Required ENV vars: +# ------------------ +# NODE_HOME - location of a Node home dir +# or +# Add /usr/local/nodejs/bin to the PATH environment variable +# ---------------------------------------------------------------------------- + +HVIGOR_APP_HOME=$(dirname $(readlink -f $0)) +HVIGOR_WRAPPER_SCRIPT=${HVIGOR_APP_HOME}/hvigor/hvigor-wrapper.js +warn() { + echo "" + echo -e "\033[1;33m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" +} + +error() { + echo "" + echo -e "\033[1;31m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" +} + +fail() { + error "$@" + exit 1 +} + +# Determine node to start hvigor wrapper script +if [ -n "${NODE_HOME}" ];then + EXECUTABLE_NODE="${NODE_HOME}/bin/node" + if [ ! -x "$EXECUTABLE_NODE" ];then + fail "ERROR: NODE_HOME is set to an invalid directory,check $NODE_HOME\n\nPlease set NODE_HOME in your environment to the location where your nodejs installed" + fi +else + EXECUTABLE_NODE="node" + which ${EXECUTABLE_NODE} > /dev/null 2>&1 || fail "ERROR: NODE_HOME is not set and not 'node' command found in your path" +fi + +# Check hvigor wrapper script +if [ ! -r "$HVIGOR_WRAPPER_SCRIPT" ];then + fail "ERROR: Couldn't find hvigor/hvigor-wrapper.js in ${HVIGOR_APP_HOME}" +fi + +# start hvigor-wrapper script +exec "${EXECUTABLE_NODE}" \ + "${HVIGOR_WRAPPER_SCRIPT}" "$@" diff --git a/hvigorw.bat b/hvigorw.bat new file mode 100644 index 0000000..29196b4 --- /dev/null +++ b/hvigorw.bat @@ -0,0 +1,57 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Hvigor startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +set WRAPPER_MODULE_PATH=%APP_HOME%\hvigor\hvigor-wrapper.js +set NODE_EXE=node.exe + +goto start + +:start +@rem Find node.exe +if defined NODE_HOME goto findNodeFromNodeHome + +%NODE_EXE% --version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. +echo. +echo Please set the NODE_HOME variable in your environment to match the +echo location of your NodeJs installation. + +goto fail + +:findNodeFromNodeHome +set NODE_HOME=%NODE_HOME:"=% +set NODE_EXE_PATH=%NODE_HOME%/%NODE_EXE% + +if exist "%NODE_EXE_PATH%" goto execute +echo. +echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. +echo. +echo Please set the NODE_HOME variable in your environment to match the +echo location of your NodeJs installation. + +goto fail + +:execute +@rem Execute hvigor +"%NODE_EXE%" %WRAPPER_MODULE_PATH% %* + +:fail +exit /b 1 diff --git a/library/index.ets b/library/index.ets index 4576c08..058e860 100644 --- a/library/index.ets +++ b/library/index.ets @@ -18,9 +18,9 @@ export { ImageKnifeAnimatorComponent } from './src/main/ets/components/ImageKnif export { ImageKnife } from './src/main/ets/ImageKnife' -export { ImageKnifeOption , AnimatorOption } from './src/main/ets/ImageKnifeOption' +export { ImageKnifeOption , AnimatorOption } from './src/main/ets/model/ImageKnifeOption' -export { ImageKnifeRequest } from './src/main/ets/ImageKnifeRequest' +export { ImageKnifeRequest } from './src/main/ets/model/ImageKnifeRequest' export { FileUtils } from './src/main/ets/utils/FileUtils' @@ -66,4 +66,6 @@ export { CropTransformation } from './src/main/ets/transform/CropTransformation' export { MaskTransformation } from './src/main/ets/transform/MaskTransformation' -export { SepiaTransformation } from './src/main/ets/transform/SepiaTransformation' \ No newline at end of file +export { SepiaTransformation } from './src/main/ets/transform/SepiaTransformation' + +export { DownsampleStrategy } from './src/main/ets/downsampling/DownsampleStartegy' \ No newline at end of file diff --git a/library/oh-package.json5 b/library/oh-package.json5 index 67af1d6..ca33846 100644 --- a/library/oh-package.json5 +++ b/library/oh-package.json5 @@ -14,7 +14,7 @@ "main": "index.ets", "repository": "https://gitee.com/openharmony-tpc/ImageKnife", "type": "module", - "version": "3.0.1-rc.2", + "version": "3.1.1-rc.0", "dependencies": { "@ohos/gpu_transform": "^1.0.2" }, diff --git a/library/src/main/ets/ImageKnife.ets b/library/src/main/ets/ImageKnife.ets index d168038..79c9fe6 100644 --- a/library/src/main/ets/ImageKnife.ets +++ b/library/src/main/ets/ImageKnife.ets @@ -12,14 +12,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeRequest } from './ImageKnifeRequest'; +import { ImageKnifeRequest } from './model/ImageKnifeRequest'; import { CacheStrategy, ImageKnifeData, ImageKnifeRequestSource } from './model/ImageKnifeData'; -import { MemoryLruCache } from './utils/MemoryLruCache'; -import { IMemoryCache } from './utils/IMemoryCache' -import { FileCache } from './utils/FileCache'; +import { MemoryLruCache } from './cache/MemoryLruCache'; +import { IMemoryCache } from './cache/IMemoryCache' +import { FileCache } from './cache/FileCache'; import { ImageKnifeDispatcher } from './ImageKnifeDispatcher'; import { IEngineKey } from './key/IEngineKey'; -import { HeaderOptions, ImageKnifeOption } from './ImageKnifeOption'; +import { HeaderOptions, ImageKnifeOption } from './model/ImageKnifeOption'; import { FileTypeUtil } from './utils/FileTypeUtil'; import { util } from '@kit.ArkTS'; import { image } from '@kit.ImageKit'; @@ -191,10 +191,10 @@ export class ImageKnife { */ getCacheImage(loadSrc: string, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string): Promise { - let option: ImageKnifeOption = { + let option: ImageKnifeOption = new ImageKnifeOption({ loadSrc: loadSrc, signature:signature - } + }) let engineKeyImpl: IEngineKey = this.getEngineKeyImpl(); return new Promise((resolve, reject) => { @@ -218,7 +218,7 @@ export class ImageKnife { */ putCacheImage(url: string, pixelMap: PixelMap, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string) { let memoryKey = this.getEngineKeyImpl() - .generateMemoryKey(url, ImageKnifeRequestSource.SRC, { loadSrc: url, signature: signature }); + .generateMemoryKey(url, ImageKnifeRequestSource.SRC, new ImageKnifeOption({ loadSrc: url, signature: signature })); let fileKey = this.getEngineKeyImpl().generateFileKey(url, signature); let imageKnifeData: ImageKnifeData = { source: pixelMap, imageWidth: 0, imageHeight: 0 }; switch (cacheType) { @@ -252,9 +252,9 @@ export class ImageKnife { if (url instanceof ImageKnifeOption) { imageKnifeOption = url; } else { - imageKnifeOption = { + imageKnifeOption = new ImageKnifeOption({ loadSrc: url - }; + }); } let key = this.getEngineKeyImpl().generateFileKey(imageKnifeOption.loadSrc, imageKnifeOption.signature); if (this.fileCache !== undefined) { @@ -302,6 +302,66 @@ export class ImageKnife { return this.fileCache as FileCache } + /** + * get cache upper limit + * @param cacheType + * @returns + */ + getCacheUpperLimit(cacheType?: CacheStrategy): number | undefined { + if (cacheType == undefined || cacheType == CacheStrategy.Default) { + cacheType = CacheStrategy.Memory; + } + if (cacheType == CacheStrategy.Memory) { + return (this.memoryCache as MemoryLruCache).maxMemory; + } else { + if (this.isFileCacheInit()) { + return this.fileCache?.maxMemory; + } else { + throw new Error("the disk cache not init"); + } + } + } + + /** + * gets the number of images currently cached + * @param cacheType + * @returns + */ + getCurrentPicturesNum(cacheType: CacheStrategy): number | undefined { + if (cacheType == undefined || cacheType == CacheStrategy.Default) { + cacheType = CacheStrategy.Memory; + } + if (cacheType == CacheStrategy.Memory) { + return (this.memoryCache as MemoryLruCache).size(); + } else { + if (this.isFileCacheInit()) { + return this.fileCache?.size(); + } else { + throw new Error("the disk cache not init"); + } + } + } + + /** + * gets the current cache size + * @param cacheType + * @returns + */ + getCurrentCacheSize(cacheType: CacheStrategy): number | undefined { + if (cacheType == undefined || cacheType == CacheStrategy.Default) { + cacheType = CacheStrategy.Memory; + } + if (cacheType == CacheStrategy.Memory) { + return (this.memoryCache as MemoryLruCache).currentMemory; + } else { + if (this.isFileCacheInit()) { + return this.fileCache?.currentMemory; + } else { + throw new Error("the disk cache not init"); + } + } + } + private pixelMapToArrayBuffer(pixelMap: PixelMap): ArrayBuffer { let imageInfo = pixelMap.getImageInfoSync(); diff --git a/library/src/main/ets/ImageKnifeDispatcher.ets b/library/src/main/ets/ImageKnifeDispatcher.ets index 795d709..e2e1466 100644 --- a/library/src/main/ets/ImageKnifeDispatcher.ets +++ b/library/src/main/ets/ImageKnifeDispatcher.ets @@ -12,24 +12,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeRequest, ImageKnifeRequestState } from './ImageKnifeRequest' -import { DefaultJobQueue } from './utils/DefaultJobQueue' -import { IJobQueue } from './utils/IJobQueue' +import { ImageKnifeRequest, ImageKnifeRequestState } from './model/ImageKnifeRequest' +import { DefaultJobQueue } from './queue/DefaultJobQueue' +import { IJobQueue } from './queue/IJobQueue' import List from '@ohos.util.List'; import LightWeightMap from '@ohos.util.LightWeightMap'; import { LogUtil } from './utils/LogUtil'; -import buffer from '@ohos.buffer'; -import { FileCache } from './utils/FileCache'; -import fs from '@ohos.file.fs'; import { ImageKnife } from './ImageKnife'; import { ImageKnifeData, CacheStrategy } from './model/ImageKnifeData'; -import http from '@ohos.net.http'; import image from '@ohos.multimedia.image'; import emitter from '@ohos.events.emitter'; import { Constants } from './utils/Constants'; import taskpool from '@ohos.taskpool'; import { FileTypeUtil } from './utils/FileTypeUtil'; -import util from '@ohos.util'; import { IEngineKey } from './key/IEngineKey'; import { DefaultEngineKey } from './key/DefaultEngineKey'; import { @@ -38,8 +33,10 @@ import { RequestJobResult, RequestJobRequest } from './model/ImageKnifeData' -import { combineArrayBuffers } from './model/utils'; import { BusinessError } from '@kit.BasicServicesKit'; +import { ImageKnifeLoader } from './ImageKnifeLoader' +import { DownsampleStrategy } from './downsampling/DownsampleStartegy'; + export class ImageKnifeDispatcher { // 最大并发 @@ -52,7 +49,7 @@ export class ImageKnifeDispatcher { private engineKey: IEngineKey = new DefaultEngineKey(); showFromMemomry(request: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,isAnimator?: boolean): boolean { - LogUtil.log("ImageKnife_DataTime_showFromMemomry.start:" + request.imageKnifeOption.loadSrc) + LogUtil.log("ImageKnife_DataTime_showFromMemomry.start:" + request.imageKnifeOption.loadSrc + "requestSource=" + requestSource + " isAnimator=" + isAnimator) let memoryCache: ImageKnifeData | undefined; if ((typeof (request.imageKnifeOption.loadSrc as image.PixelMap).isEditable) == 'boolean') { memoryCache = { @@ -65,13 +62,12 @@ export class ImageKnifeDispatcher { .loadFromMemoryCache(this.engineKey.generateMemoryKey(imageSrc, requestSource, request.imageKnifeOption,isAnimator)); } - if (memoryCache !== undefined) { // 画主图 if (request.requestState === ImageKnifeRequestState.PROGRESS) { // 回调请求开始 if (requestSource === ImageKnifeRequestSource.SRC && request.imageKnifeOption.onLoadListener?.onLoadStart !== undefined) { - request.imageKnifeOption.onLoadListener?.onLoadStart() + request.imageKnifeOption.onLoadListener.onLoadStart() LogUtil.log("ImageKnife_DataTime_MemoryCache_onLoadStart:" + request.imageKnifeOption.loadSrc) } LogUtil.log("ImageKnife_DataTime_MemoryCache_showPixelMap.start:" + request.imageKnifeOption.loadSrc) @@ -82,23 +78,22 @@ export class ImageKnifeDispatcher { request.requestState = ImageKnifeRequestState.COMPLETE // 回调请求开结束 if (request.imageKnifeOption.onLoadListener?.onLoadSuccess !== undefined) { - request.imageKnifeOption.onLoadListener?.onLoadSuccess(memoryCache.source,memoryCache) + request.imageKnifeOption.onLoadListener.onLoadSuccess(memoryCache.source,memoryCache) LogUtil.log("ImageKnife_DataTime_MemoryCache_onLoadSuccess:" + request.imageKnifeOption.loadSrc) } } else if (requestSource == ImageKnifeRequestSource.ERROR_HOLDER) { request.requestState = ImageKnifeRequestState.ERROR } } - LogUtil.log("ImageKnife_DataTime_showFromMemomry.end_true:" + request.imageKnifeOption.loadSrc) + LogUtil.log("ImageKnife_DataTime_showFromMemomry.end_hasmemory:" + request.imageKnifeOption.loadSrc) return true } - LogUtil.log("ImageKnife_DataTime_showFromMemomry.end_false:" + request.imageKnifeOption.loadSrc) + LogUtil.log("ImageKnife_DataTime_showFromMemomry.end_nomemory:" + request.imageKnifeOption.loadSrc) return false } enqueue(request: ImageKnifeRequest,isAnimator?: boolean): void { - //1.内存有的话直接渲染 if (this.showFromMemomry(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC,isAnimator)) { return @@ -157,10 +152,21 @@ export class ImageKnifeDispatcher { isWatchProgress = true } }); - + let src: string | number = "" + let moduleName: string = "" + let resName: string = "" + if((imageSrc as Resource).id != undefined) { + moduleName = (imageSrc as Resource).moduleName + src = (imageSrc as Resource).id + if(src == -1) { + resName = (imageSrc as Resource).params![0] + } + } else if(typeof imageSrc == "string") { + src = imageSrc + } let request: RequestJobRequest = { context: currentRequest.context, - src: imageSrc, + src: src, headers: currentRequest.imageKnifeOption.headerOption, allHeaders: currentRequest.headers, componentWidth:currentRequest.componentWidth, @@ -175,8 +181,13 @@ export class ImageKnifeDispatcher { requestSource: requestSource, isWatchProgress: isWatchProgress, memoryKey: memoryKey, - fileCacheFolder: ImageKnife.getInstance().getFileCache().getCacheFolder(), - isAnimator:isAnimator + fileCacheFolder: ImageKnife.getInstance().getFileCache()?.getCacheFolder(), + isAnimator:isAnimator, + moduleName: moduleName == "" ? undefined : moduleName, + resName: resName == "" ? undefined : resName, + targetWidth: currentRequest.componentWidth, + targetHeight: currentRequest.componentHeight, + downsampType: currentRequest.imageKnifeOption.downsampleOf==undefined?DownsampleStrategy.NONE:currentRequest.imageKnifeOption.downsampleOf, } if(request.customGetImage == undefined) { @@ -191,9 +202,9 @@ export class ImageKnifeDispatcher { emitter.on(Constants.PROGRESS_EMITTER + memoryKey, (data) => { this.progressCallBack(requestList! , data?.data?.value as number) }); - } + } - LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start:" + currentRequest.imageKnifeOption.loadSrc) + LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start(subthread):" + currentRequest.imageKnifeOption.loadSrc) taskpool.execute(task).then((res: Object) => { this.doTaskCallback(res as RequestJobResult | undefined, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); if (isWatchProgress){ @@ -201,8 +212,9 @@ export class ImageKnifeDispatcher { } LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc) LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc) - }).catch((err:BusinessError)=>{ - LogUtil.error("Fail to execute in sub thread src=" + imageSrc + " err=" + err) + }).catch((err: BusinessError) => { + LogUtil.error("Fail to requestJob in sub thread src=" + imageSrc + " err=" + err) + LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:" + currentRequest.imageKnifeOption.loadSrc) if (isWatchProgress){ emitter.off(Constants.PROGRESS_EMITTER + memoryKey) } @@ -210,12 +222,14 @@ export class ImageKnifeDispatcher { this.dispatchNextJob(); }) } else { //主线程请求 + LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start(mainthread):" + currentRequest.imageKnifeOption.loadSrc) requestJob(request, requestList).then((res: RequestJobResult | undefined) => { this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc) LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc) - }).catch((err:BusinessError)=>{ - LogUtil.error("Fail to execute in main thread src=" + imageSrc + " err=" + err) + }).catch((err: BusinessError) => { + LogUtil.error("Fail to requestJob in main thread src=" + imageSrc + " err=" + err) + LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:" + currentRequest.imageKnifeOption.loadSrc) this.executingJobMap.remove(memoryKey); this.dispatchNextJob(); }) @@ -244,6 +258,7 @@ export class ImageKnifeDispatcher { } let pixelmap = requestJobResult.pixelMap; if (pixelmap === undefined) { + LogUtil.log("ImageKnife_DataTime_getAndShowImage_CallBack.pixelmap undefined:"+currentRequest.imageKnifeOption.loadSrc) requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => { // 回调请求失败 if (requestWithSource.source === ImageKnifeRequestSource.SRC && @@ -263,6 +278,7 @@ export class ImageKnifeDispatcher { } }); this.executingJobMap.remove(memoryKey); + this.dispatchNextJob(); return; } // 保存文件缓存 @@ -297,8 +313,6 @@ export class ImageKnifeDispatcher { LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveMemoryCache.end:"+currentRequest.imageKnifeOption.loadSrc) } if (requestList !== undefined) { - - // todo 判断request生命周期,已销毁的不需要再绘制 // key相同的request,一起绘制 requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => { if (requestWithSource.request.requestState !== ImageKnifeRequestState.DESTROY) { @@ -326,8 +340,8 @@ export class ImageKnifeDispatcher { } } else { if (requestWithSource.source == ImageKnifeRequestSource.SRC && requestWithSource.request.imageKnifeOption.onLoadListener?.onLoadCancel) { - // 回调请求成功 - requestWithSource.request.imageKnifeOption.onLoadListener.onLoadCancel("component has destroyed") + // 回调请求成功 + requestWithSource.request.imageKnifeOption.onLoadListener.onLoadCancel("component has destroyed") } } }); @@ -346,11 +360,13 @@ export class ImageKnifeDispatcher { while (true) { let request = this.jobQueue.pop() if (request === undefined) { + LogUtil.log("ImageKnife_DataTime_dispatchNextJob.end:no any job") break // 队列已无任务 } else if (request.requestState === ImageKnifeRequestState.PROGRESS) { + LogUtil.log("ImageKnife_DataTime_dispatchNextJob.start executeJob:" + request.imageKnifeOption.loadSrc) this.executeJob(request) - LogUtil.log("ImageKnife_DataTime_dispatchNextJob.end:" + request.imageKnifeOption.loadSrc) + LogUtil.log("ImageKnife_DataTime_dispatchNextJob.end executeJob:" + request.imageKnifeOption.loadSrc) break }else if (request.requestState == ImageKnifeRequestState.DESTROY && request.imageKnifeOption.onLoadListener?.onLoadCancel) { request.imageKnifeOption.onLoadListener.onLoadCancel("component has destroyed") @@ -381,288 +397,44 @@ export class ImageKnifeDispatcher { */ @Concurrent async function requestJob(request: RequestJobRequest, requestList?: List): Promise { - LogUtil.log("ImageKnife_DataTime_requestJob.start:" + request.src) - let resBuf: ArrayBuffer | undefined - let bufferSize: number = 0 - let loadError: string = ''; + LogUtil.log("ImageKnife_DataTime_requestJob.start:" + request.src + " requestSource=" + request.requestSource) + let src = typeof request.src == "number" ? request.resName != undefined ? request.resName : request.src + "" : request.src + // 生成文件缓存key + let fileKey = request.engineKey.generateFileKey(src, request.signature, request.isAnimator) - class RequestData { - receiveSize: number = 2000 - totalSize: number = 2000 + //获取图片资源 + let resBuf: ArrayBuffer + try { + LogUtil.log("ImageKnife_DataTime_requestJob.getImageArrayBuffer.start:" + request.src) + resBuf = await ImageKnifeLoader.getImageArrayBuffer(request, requestList, fileKey) + LogUtil.log("ImageKnife_DataTime_requestJob.getImageArrayBuffer.end:" + request.src) + } catch (error) { + LogUtil.error("ImageKnife_DataTime_requestJob.end: getImageArrayBuffer error " + request.src + " err=" + error) + return ImageKnifeLoader.makeEmptyResult(error) } - // 生成文件key - let fileKey = request.engineKey.generateFileKey(request.src, request.signature,request.isAnimator) - - // 判断自定义下载 - if (request.customGetImage !== undefined && request.requestSource == ImageKnifeRequestSource.SRC) { - // 先从文件缓存获取 - resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder) - if (resBuf === undefined) { - LogUtil.log("customGetImage customGetImage"); - resBuf = await request.customGetImage(request.context, request.src) - loadError = resBuf == undefined ? "customGetImage loadFile" : loadError - // 保存文件缓存 - if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) { - let copyBuf = buffer.concat([buffer.from(resBuf)]).buffer; // IDE有bug,不能直接获取resBuf.byteLength - bufferSize = copyBuf.byteLength - FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf , request.fileCacheFolder) - } - } - } - else { - if (typeof request.src === 'string') { - if (request.src.indexOf("http://") == 0 || request.src.indexOf("https://") == 0) { //从网络下载 - // 先从文件缓存获取 - resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder) - if (resBuf === undefined && request.onlyRetrieveFromCache != true) { - LogUtil.log("ImageKnife_DataTime_requestJob_httpRequest.start:"+request.src) - let httpRequest = http.createHttp(); - let progress: number = 0 - let arrayBuffers = new Array() - const headerObj: Record = {} - if (request.headers != undefined) { - request.headers.forEach((value) => { - headerObj[value.key] = value.value - }) - } else if (request.allHeaders.size > 0) { - request.allHeaders.forEach((value, key) => { - headerObj[key] = value - }) - } - httpRequest.on("dataReceive", (data: ArrayBuffer) => { - arrayBuffers.push(data) - }); - - if (request.isWatchProgress) { - httpRequest.on('dataReceiveProgress', (data: RequestData) => { - // 下载进度 - if (data != undefined && (typeof data.receiveSize == 'number') && (typeof data.totalSize == 'number')) { - let percent = Math.round(((data.receiveSize * 1.0) / (data.totalSize * 1.0)) * 100) - if (progress !== percent) { - progress = percent - if (requestList === undefined) { - // 子线程 - emitter.emit(Constants.PROGRESS_EMITTER + request.memoryKey, { data: { "value": progress } }) - }else { - // 主线程请求 - requestList!.forEach((requestWithSource: ImageKnifeRequestWithSource) => { - if (requestWithSource.request.imageKnifeOption.progressListener !== undefined && requestWithSource.source === ImageKnifeRequestSource.SRC) { - requestWithSource.request.imageKnifeOption.progressListener(progress) - } - }) - } - } - } - }) - } - let promise = httpRequest.requestInStream(request.src, { - header: headerObj, - method: http.RequestMethod.GET, - expectDataType: http.HttpDataType.ARRAY_BUFFER, - connectTimeout: 6000, - readTimeout: 6000, - // usingProtocol:http.HttpProtocol.HTTP1_1 - // header: new Header('application/json') - }); - - await promise.then((data: number) => { - if (data == 200) { - resBuf = combineArrayBuffers(arrayBuffers) - } else { - loadError = "HttpDownloadClient has error, http code =" + JSON.stringify(data) - } - }).catch((err: Error) => { - loadError = err.message; - LogUtil.error("requestInStream ERROR : err = " + JSON.stringify(err)); - }); - LogUtil.log("ImageKnife_DataTime_requestJob_httpRequest.end:"+request.src) - // 保存文件缓存 - if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) { - LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:"+request.src) - let copyBuf = combineArrayBuffers(arrayBuffers); // IDE有bug,不能直接获取resBuf.byteLength - bufferSize = copyBuf.byteLength - FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf , request.fileCacheFolder) - LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:"+request.src) - } - } - else { - LogUtil.log("success get image from filecache for key = " + fileKey); - loadError = "success get image from filecache for key = " + fileKey; - } - } else if (request.src.startsWith('datashare://') || request.src.startsWith('file://')) { - await fs.open(request.src, fs.OpenMode.READ_ONLY).then(async (file) => { - await fs.stat(file.fd).then(async (stat) =>{ - let buf = new ArrayBuffer(stat.size); - await fs.read(file.fd, buf).then((readLen) => { - resBuf = buf; - fs.close(file.fd); - }).catch((err:BusinessError) => { - loadError = 'LoadDataShareFileClient fs.read err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code; - }) - }).catch((err:BusinessError) => { - loadError = 'LoadDataShareFileClient fs.stat err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code; - }) - }).catch((err:BusinessError) => { - loadError ='LoadDataShareFileClient fs.open err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code; - }) - } else { //从本地文件获取 - try { - let stat = fs.statSync(request.src); - if (stat.size > 0) { - let file = fs.openSync(request.src, fs.OpenMode.READ_ONLY); - resBuf = new ArrayBuffer(stat.size); - fs.readSync(file.fd, resBuf); - fs.closeSync(file); - } - } catch (err) { - if (typeof err == 'string') { - loadError = err; - } else { - loadError = err.message; - } - } - } - } else if ((request.src as Resource).id !== undefined) { //从资源文件获取 - let res = request.src as Resource; - let manager = request.context.createModuleContext(res.moduleName).resourceManager - if (resBuf == undefined && request.onlyRetrieveFromCache != true && request.requestSource == ImageKnifeRequestSource.SRC) { - if(res.id == -1) { - let resName = (res.params![0] as string) - resBuf = (await manager.getMediaByName(resName.substring(10))).buffer as ArrayBuffer - } else { - resBuf = manager.getMediaContentSync(res.id).buffer as ArrayBuffer - } - } else if (resBuf == undefined && request.requestSource != ImageKnifeRequestSource.SRC) { - if(res.id == -1) { - let resName = (res.params![0] as string) - resBuf = (await manager.getMediaByName(resName.substring(10))).buffer as ArrayBuffer - } else { - resBuf = manager.getMediaContentSync(res.id).buffer as ArrayBuffer - } - } - } - } - - - if (resBuf == undefined) { - LogUtil.log("ImageKnife_DataTime_requestJob.end_undefined:"+request.src) - return { - pixelMap: undefined, - bufferSize: 0, - fileKey: '', - loadFail: loadError, - } - } - LogUtil.log("ImageKnife_DataTime_requestJob_createPixelMap.start:"+request.src) - let fileTypeUtil = new FileTypeUtil(); - let typeValue = fileTypeUtil.getFileType(resBuf); + // 获取图片类型 + let typeValue = new FileTypeUtil().getFileType(resBuf); if(typeValue == null) { - return { - pixelMap: undefined, - bufferSize: 0, - fileKey: '', - loadFail: "request is not a valid image source", - } + LogUtil.log("ImageKnife_DataTime_requestJob.end: getFileType is null " + request.src) + return ImageKnifeLoader.makeEmptyResult("request is not a valid image source") } - let imageSource: image.ImageSource = image.createImageSource(resBuf); - let decodingOptions: image.DecodingOptions = { - editable: true, - } - if(request.isAnimator) { - if (typeValue === 'gif' || typeValue === 'webp') { - let pixelMapList: Array = [] - let delayList: Array = [] - await imageSource.createPixelMapList(decodingOptions).then(async (pixelList: Array) => { - //sdk的api接口发生变更:从.getDelayTime() 变为.getDelayTimeList() - await imageSource.getDelayTimeList().then(delayTimes => { - if (pixelList.length > 0) { - for (let i = 0; i < pixelList.length; i++) { - pixelMapList.push(pixelList[i]); - if (i < delayTimes.length) { - delayList.push(delayTimes[i]); - } else { - delayList.push(delayTimes[delayTimes.length - 1]) - } - } - imageSource.release(); - } - }) - }) - return { - pixelMap: "", - bufferSize: bufferSize, - fileKey: fileKey, - type: typeValue, - pixelMapList, - delayList - } - } else { - return { - pixelMap: undefined, - bufferSize: 0, - fileKey: '', - loadFail: "ImageKnifeAnimatorComponent组件仅支持动态图", - } - } - } - let resPixelmap: PixelMap | undefined = undefined - if (typeValue === 'gif' || typeValue === 'webp') { - let size = (await imageSource.getImageInfo()).size - let base64Help = new util.Base64Helper() - let base64str = "data:image/" + typeValue + ";base64," + base64Help.encodeToStringSync(new Uint8Array(resBuf)) - LogUtil.log("ImageKnife_DataTime_requestJob_createPixelMap.end_GIF:"+request.src) - LogUtil.log("ImageKnife_DataTime_requestJob.end_GIF:"+request.src) - return { - pixelMap: base64str, - bufferSize: bufferSize, - fileKey: fileKey, - size:size, - type:typeValue - }; - } else if(typeValue == "svg") { - let hValue = Math.round(request.componentHeight); - let wValue = Math.round(request.componentWidth); - let defaultSize: image.Size = { - height: vp2px(hValue), - width: vp2px(wValue) - }; - let opts: image.DecodingOptions = { - editable: true, - desiredSize: defaultSize - }; - await imageSource.createPixelMap(opts) - .then((pixelmap: PixelMap) => { - resPixelmap = pixelmap - imageSource.release() - }) - return { - pixelMap: resPixelmap, - bufferSize: bufferSize, - fileKey: fileKey, - type:typeValue - }; - } - let size = (await imageSource.getImageInfo()).size - await imageSource.createPixelMap(decodingOptions) - .then((pixelmap: PixelMap) => { - resPixelmap = pixelmap - imageSource.release() - }) + // 解析图片 + LogUtil.log("ImageKnife_DataTime_requestJob.parseImage.start:" + request.src) + let result: RequestJobResult = await ImageKnifeLoader.parseImage(resBuf, typeValue, fileKey, request) + LogUtil.log("ImageKnife_DataTime_requestJob.parseImage.end:" + request.src) // 图形变化 - if (request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined) { - resPixelmap = await request.transformation?.transform(request.context, resPixelmap!, request.componentWidth, request.componentHeight); + if (request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined && result?.pixelMap !== undefined && typeof result.pixelMap !== 'string') { + LogUtil.log("ImageKnife_DataTime_requestJob.transform.start:" + request.src) + result.pixelMap = await request.transformation?.transform(request.context, result.pixelMap, request.componentWidth, request.componentHeight); + LogUtil.log("ImageKnife_DataTime_requestJob.transform.end:" + request.src) } - LogUtil.log("ImageKnife_DataTime_requestJob_createPixelMap.end:"+request.src) - LogUtil.log("ImageKnife_DataTime_requestJob.end:"+request.src) - return { - pixelMap: resPixelmap, - bufferSize: bufferSize, - fileKey: fileKey, - size:size, - type:typeValue - }; + + LogUtil.log("ImageKnife_DataTime_requestJob.end:" + request.src) + return result } + + diff --git a/library/src/main/ets/ImageKnifeLoader.ets b/library/src/main/ets/ImageKnifeLoader.ets new file mode 100644 index 0000000..b00e5fb --- /dev/null +++ b/library/src/main/ets/ImageKnifeLoader.ets @@ -0,0 +1,402 @@ +/* + * 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 { + CacheStrategy, + ImageKnifeRequestSource, + ImageKnifeRequestWithSource, RequestJobRequest } from './model/ImageKnifeData'; +import List from '@ohos.util.List' +import { FileCache } from './cache/FileCache'; +import { LogUtil } from './utils/LogUtil'; +import { Constants } from './utils/Constants'; +import http from '@ohos.net.http'; +import { combineArrayBuffers } from './utils/ArrayBufferUtils'; +import { BusinessError } from '@kit.BasicServicesKit'; +import fs from '@ohos.file.fs'; +import emitter from '@ohos.events.emitter'; +import image from '@ohos.multimedia.image'; +import { RequestJobResult } from './model/ImageKnifeData' +import util from '@ohos.util'; +import { DownsampleStrategy } from './downsampling/DownsampleStartegy'; +import { Downsampler } from './downsampling/Downsampler'; + +class RequestData { + receiveSize: number = 2000 + totalSize: number = 2000 +} + +/** + * ImageKnifeDispatcher 抽取出来的方法,因@Concurrent只能import方法,故抽取到另一个类 + */ +export class ImageKnifeLoader { + static async parseImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, + request: RequestJobRequest): Promise { + if(request.isAnimator) { + return ImageKnifeLoader.parseForAnimatorComponent(resBuf ,typeValue ,fileKey, request) + } + + if (typeValue === 'gif' || typeValue === 'webp') { + return ImageKnifeLoader.parseAnimatorImage(resBuf ,typeValue ,fileKey , request) + } else if(typeValue == "svg") { + return ImageKnifeLoader.parseSvgImage(resBuf ,typeValue ,fileKey , request) + } + + return ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request) + } + + static makeEmptyResult(error: string): RequestJobResult{ + return { + pixelMap: undefined, + bufferSize: 0, + fileKey: '', + loadFail: error, + } + } + + static async parseNormalImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, request: RequestJobRequest):Promise { + let resPixelmap: PixelMap | undefined = undefined + let decodingOptions: image.DecodingOptions = { + editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true : false, + } + let imageSource: image.ImageSource = image.createImageSource(resBuf) + if (imageSource === undefined){ + return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") + } + + let size = (await imageSource.getImageInfo()).size + try { + if ((request.downsampType !== DownsampleStrategy.NONE) && + request.requestSource == ImageKnifeRequestSource.SRC) { + decodingOptions = ImageKnifeLoader.getDownsamplerDecodingOptions(typeValue, request, size, ImageKnifeRequestSource.SRC) + } + } catch (err) { + return ImageKnifeLoader.makeEmptyResult(err) + } + + await imageSource.createPixelMap(decodingOptions) + .then((pixelmap: PixelMap) => { + resPixelmap = pixelmap + imageSource.release() + }).catch((error: BusinessError) => { + imageSource.release() + return ImageKnifeLoader.makeEmptyResult(JSON.stringify(error)) + }) + + return { + pixelMap: resPixelmap, + bufferSize: resBuf.byteLength, + fileKey: fileKey, + size:size, + type:typeValue + }; + } + static async parseSvgImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, + request: RequestJobRequest): Promise { + let resPixelmap: PixelMap | undefined = undefined + let imageSource: image.ImageSource = image.createImageSource(resBuf) + if (imageSource === undefined){ + return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") + } + + let size = (await imageSource.getImageInfo()).size + let scale = size.height / size.width + let hValue = Math.round(request.componentHeight); + let wValue = Math.round(request.componentWidth); + let defaultSize: image.Size = { + height: vp2px(wValue) * scale, + width: vp2px(wValue) + }; + let opts: image.DecodingOptions = { + editable: true, + desiredSize: defaultSize + }; + try { + if ((request.downsampType !== DownsampleStrategy.NONE) && + request.requestSource == ImageKnifeRequestSource.SRC) { + opts = ImageKnifeLoader.getDownsamplerDecodingOptions(typeValue, request, size) + } + } catch (err) { + return ImageKnifeLoader.makeEmptyResult(err) + } + await imageSource.createPixelMap(opts) + .then((pixelmap: PixelMap) => { + resPixelmap = pixelmap + imageSource.release() + }).catch((error: BusinessError) => { + imageSource.release() + return ImageKnifeLoader.makeEmptyResult(JSON.stringify(error)) + }) + + return { + pixelMap: resPixelmap, + bufferSize: resBuf.byteLength, + fileKey: fileKey, + type:typeValue + }; + } + static async parseAnimatorImage(resBuf: ArrayBuffer, typeValue: string, + fileKey: string,request: RequestJobRequest): Promise { + let imageSource: image.ImageSource = image.createImageSource(resBuf) + if (imageSource === undefined){ + return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") + } + + let frameCount = await imageSource.getFrameCount() + let size = (await imageSource.getImageInfo()).size + imageSource.release() + + if(frameCount == undefined || frameCount == 1) { + } else { + let base64str = "data:image/" + typeValue + ";base64," + new util.Base64Helper().encodeToStringSync(new Uint8Array(resBuf)) + return { + pixelMap: base64str, + bufferSize: resBuf.byteLength, + fileKey: fileKey, + size:size, + type:typeValue + }; + } + return ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request) + } + // 为AnimatorComponent解析动图 + static async parseForAnimatorComponent(resBuf: ArrayBuffer, typeValue: string, fileKey: string,request: RequestJobRequest): Promise { + if (typeValue === 'gif' || typeValue === 'webp') { + let imageSource: image.ImageSource = image.createImageSource(resBuf); + if (imageSource === undefined){ + return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") + } + let decodingOptions: image.DecodingOptions = { + editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true : false, + } + let pixelMapList: Array = [] + let delayList: Array = [] + await imageSource.createPixelMapList(decodingOptions).then(async (pixelList: Array) => { + //sdk的api接口发生变更:从.getDelayTime() 变为.getDelayTimeList() + await imageSource.getDelayTimeList().then(delayTimes => { + if (pixelList.length > 0) { + for (let i = 0; i < pixelList.length; i++) { + pixelMapList.push(pixelList[i]); + if (i < delayTimes.length) { + delayList.push(delayTimes[i]); + } else { + delayList.push(delayTimes[delayTimes.length - 1]) + } + } + imageSource.release(); + } + }) + }).catch((error: BusinessError) => { + imageSource.release() + return ImageKnifeLoader.makeEmptyResult(JSON.stringify(error)) + }) + return { + pixelMap: "", + bufferSize: resBuf.byteLength, + fileKey: fileKey, + type: typeValue, + pixelMapList, + delayList + } + } else { + return ImageKnifeLoader.makeEmptyResult("ImageKnifeAnimatorComponent组件仅支持动态图") + } + } + + // 获取图片资源 + static async getImageArrayBuffer(request: RequestJobRequest, requestList: List | undefined,fileKey:string): Promise { + let resBuf: ArrayBuffer | undefined + + // 判断自定义下载 + if (request.customGetImage !== undefined && request.requestSource == ImageKnifeRequestSource.SRC && typeof request.src == "string") { + // 先从文件缓存获取 + resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder) + if (resBuf === undefined) { + LogUtil.log("start customGetImage src=" + request.src) + try { + resBuf = await request.customGetImage(request.context, request.src) + LogUtil.log("end customGetImage src=" + request.src) + } catch (err) { + throw new Error('customGetImage loadFile failed! err = ' + err) + } + if (resBuf === undefined) { + throw new Error('customGetImage loadFile failed!') + } + // 保存文件缓存 + if (request.writeCacheStrategy !== CacheStrategy.Memory) { + LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:" + request.src) + FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf, request.fileCacheFolder) + LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:" + request.src) + } + } + } + else { + if (typeof request.src === 'string') { + if (request.src.indexOf("http://") == 0 || request.src.indexOf("https://") == 0) { //从网络下载 + // 先从文件缓存获取 + resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder) + if (resBuf !== undefined){ + LogUtil.log("success get image from filecache for key = " + fileKey + " src = " + request.src) + } + else if (request.onlyRetrieveFromCache != true) { + LogUtil.log("HttpDownloadClient.start:" + request.src) + let httpRequest = http.createHttp(); + let progress: number = 0 + let arrayBuffers = new Array() + const headerObj: Record = {} + if (request.headers != undefined) { + request.headers.forEach((value) => { + headerObj[value.key] = value.value + }) + } else if (request.allHeaders.size > 0) { + request.allHeaders.forEach((value, key) => { + headerObj[key] = value + }) + } + httpRequest.on("dataReceive", (data: ArrayBuffer) => { + arrayBuffers.push(data) + }); + + if (request.isWatchProgress) { + httpRequest.on('dataReceiveProgress', (data: RequestData) => { + // 下载进度 + if (data != undefined && (typeof data.receiveSize == 'number') && (typeof data.totalSize == 'number')) { + let percent = Math.round(((data.receiveSize * 1.0) / (data.totalSize * 1.0)) * 100) + if (progress !== percent) { + progress = percent + if (requestList === undefined) { + // 子线程 + emitter.emit(Constants.PROGRESS_EMITTER + request.memoryKey, { data: { "value": progress } }) + }else { + // 主线程请求 + requestList!.forEach((requestWithSource: ImageKnifeRequestWithSource) => { + if (requestWithSource.request.imageKnifeOption.progressListener !== undefined && requestWithSource.source === ImageKnifeRequestSource.SRC) { + requestWithSource.request.imageKnifeOption.progressListener(progress) + } + }) + } + } + } + }) + } + let promise = httpRequest.requestInStream(request.src, { + header: headerObj, + method: http.RequestMethod.GET, + expectDataType: http.HttpDataType.ARRAY_BUFFER, + connectTimeout: 60000, + readTimeout: 0, + // usingProtocol:http.HttpProtocol.HTTP1_1 + // header: new Header('application/json') + }); + + await promise.then((data: number) => { + if (data == 200 || data == 206 || data == 204) { + resBuf = combineArrayBuffers(arrayBuffers) + } else { + throw new Error("HttpDownloadClient has error, http code =" + JSON.stringify(data)) + } + }).catch((err: Error) => { + throw new Error("HttpDownloadClient download ERROR : err = " + JSON.stringify(err)) + }); + LogUtil.log("HttpDownloadClient.end:" + request.src) + // 保存文件缓存 + if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) { + LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:"+request.src) + FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf , request.fileCacheFolder) + LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:"+request.src) + } + } + else { + throw new Error('onlyRetrieveFromCache,do not fetch image src = ' + request.src) + } + } else if (request.src.startsWith('datashare://') || request.src.startsWith('file://')) { + await fs.open(request.src, fs.OpenMode.READ_ONLY).then(async (file) => { + await fs.stat(file.fd).then(async (stat) =>{ + let buf = new ArrayBuffer(stat.size); + await fs.read(file.fd, buf).then((readLen) => { + resBuf = buf; + fs.closeSync(file.fd); + }).catch((err:BusinessError) => { + throw new Error('LoadDataShareFileClient fs.read err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code) + }) + }).catch((err:BusinessError) => { + throw new Error('LoadDataShareFileClient fs.stat err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code) + }) + }).catch((err:BusinessError) => { + throw new Error('LoadDataShareFileClient fs.open err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code) + }) + } else { //从本地文件获取 + try { + let stat = fs.statSync(request.src); + if (stat.size > 0) { + let file = fs.openSync(request.src, fs.OpenMode.READ_ONLY); + resBuf = new ArrayBuffer(stat.size); + fs.readSync(file.fd, resBuf); + fs.closeSync(file); + } + } catch (err) { + throw new Error(err) + } + } + } else if (typeof request.src == "number") { //从资源文件获取 + let manager = request.context.createModuleContext(request.moduleName).resourceManager + if (resBuf == undefined && request.onlyRetrieveFromCache != true && request.requestSource == ImageKnifeRequestSource.SRC) { + if(request.src == -1) { + let resName = request.resName as string + resBuf = (await manager.getMediaByName(resName.substring(10))).buffer as ArrayBuffer + } else { + resBuf = manager.getMediaContentSync(request.src).buffer as ArrayBuffer + } + } else if (resBuf == undefined && request.requestSource != ImageKnifeRequestSource.SRC) { + if(request.src == -1) { + let resName = request.resName as string + resBuf = (await manager.getMediaByName(resName.substring(10))).buffer as ArrayBuffer + } else { + resBuf = manager.getMediaContentSync(request.src).buffer as ArrayBuffer + } + } + } + } + + if (resBuf === undefined){ + throw new Error('getImageArrayBuffer undefined') + } + return resBuf + } + + static getDownsamplerDecodingOptions(typeValue: string, request: RequestJobRequest, size: Size, + SRC?: ImageKnifeRequestSource):image.DecodingOptions { + let reqSize = + new Downsampler().calculateScaling(typeValue, size.width, size.height, request.targetWidth, request.targetHeight, + request.downsampType) + if (typeValue == "svg") { + return { + editable: true, + desiredSize: { + height: vp2px(reqSize.height), + width: vp2px(reqSize.width) + } + + } + } else { + return { + editable: request.requestSource === SRC && request.transformation !== undefined ? true : false, + desiredSize:{ + width: reqSize.width, + height: reqSize.height + } + } + } + } +} + diff --git a/library/src/main/ets/ImageKnifeOption.ets b/library/src/main/ets/ImageKnifeOption.ets deleted file mode 100644 index 438849e..0000000 --- a/library/src/main/ets/ImageKnifeOption.ets +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2024 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import taskpool from '@ohos.taskpool'; -import common from '@ohos.app.ability.common' -import { CacheStrategy, ImageKnifeData,EventImage } from './model/ImageKnifeData'; -import { PixelMapTransformation } from './transform/PixelMapTransformation'; -import { drawing } from '@kit.ArkGraphics2D'; - -export interface HeaderOptions { - key: string; - value: Object; -} - -@Observed -export class AnimatorOption { - @Track - state?: AnimationStatus = AnimationStatus.Running - @Track - iterations?: number = -1 - @Track - reverse?: boolean = false -} - -@Observed -export class ImageKnifeOption { - // 主图资源 - loadSrc: string | PixelMap | Resource = ""; - // 占位图 - placeholderSrc?: string | PixelMap | Resource; - // 失败占位图 - errorholderSrc?: string | PixelMap | Resource; - headerOption?: Array; - // 自定义缓存关键字 - signature?: string; - // 主图填充效果 - objectFit?: ImageFit - // 占位图填充效果 - placeholderObjectFit?: ImageFit - // 错误图填充效果 - errorholderObjectFit?: ImageFit - customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise - border?: BorderOptions - // 缓存策略 - writeCacheStrategy?: CacheStrategy - // 仅使用缓存加载数据 - onlyRetrieveFromCache?: boolean = false; - priority?: taskpool.Priority = taskpool.Priority.LOW - context?: common.UIAbilityContext; - progressListener?: (progress: number) => void; - transformation?: PixelMapTransformation - onLoadListener?: OnLoadCallBack | undefined; - onComplete?:(event:EventImage | undefined) => void - drawingColorFilter?: ColorFilter | drawing.ColorFilter - constructor() { - - } -} - -/** - * 请求回调 - */ -export interface OnLoadCallBack { - // 请求开始 - onLoadStart?: () => void; - - // 请求成功 - onLoadSuccess?: (data: string | PixelMap | undefined, imageKnifeData: ImageKnifeData) => void; - - // 请求结束 - onLoadFailed?: (err: string) => void; - // 请求取消 - onLoadCancel?: (reason: string) => void; -} \ No newline at end of file diff --git a/library/src/main/ets/utils/FileCache.ets b/library/src/main/ets/cache/FileCache.ets similarity index 94% rename from library/src/main/ets/utils/FileCache.ets rename to library/src/main/ets/cache/FileCache.ets index 8c47562..41978b8 100644 --- a/library/src/main/ets/utils/FileCache.ets +++ b/library/src/main/ets/cache/FileCache.ets @@ -13,12 +13,12 @@ * limitations under the License. */ import util from '@ohos.util'; -import { FileUtils } from './FileUtils'; +import { FileUtils } from '../utils/FileUtils'; import fs from '@ohos.file.fs'; -import { LogUtil } from './LogUtil'; +import { LogUtil } from '../utils/LogUtil'; import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5'; - +const INT_MAX = 2147483647 /** * 二级文件缓存 * 主线程通过lruCache管理缓存大小 @@ -34,12 +34,12 @@ export class FileCache { private isInited: boolean = false private context?: Context readonly defaultMaxSize: number = 512; - readonly defaultSize: number = 128; + readonly defaultSize: number = INT_MAX; readonly defaultMaxMemorySize: number = 512 * 1024 * 1024; readonly defaultMemorySize: number = 128 * 1024 * 1024; constructor(context: Context, size: number, memory: number) { - if (size <= 0) { + if (size <= 0 || size > INT_MAX) { size = this.defaultSize } if (memory <= 0 || memory > this.defaultMaxMemorySize) { @@ -232,18 +232,18 @@ export class FileCache { } else if (value != undefined) { this.currentMemory -= value.byteLength - LogUtil.info("FileCache removeMemorySize: " + value.byteLength + " currentMemory:" + this.currentMemory) + LogUtil.debug("FileCache removeMemorySize: " + value.byteLength + " currentMemory:" + this.currentMemory) } } private addMemorySize(value: ArrayBuffer | number): void { if (typeof value == "number") { this.currentMemory += value - LogUtil.info("FileCache addMemorySize: " + value + " currentMemory:" + this.currentMemory) + LogUtil.debug("FileCache addMemorySize: " + value + " currentMemory:" + this.currentMemory) } else if (value != undefined) { this.currentMemory += value.byteLength - LogUtil.info("FileCache addMemorySize: " + value.byteLength + " currentMemory:" + this.currentMemory) + LogUtil.debug("FileCache addMemorySize: " + value.byteLength + " currentMemory:" + this.currentMemory) } } diff --git a/library/src/main/ets/utils/IMemoryCache.ets b/library/src/main/ets/cache/IMemoryCache.ets similarity index 100% rename from library/src/main/ets/utils/IMemoryCache.ets rename to library/src/main/ets/cache/IMemoryCache.ets diff --git a/library/src/main/ets/utils/MemoryLruCache.ets b/library/src/main/ets/cache/MemoryLruCache.ets similarity index 100% rename from library/src/main/ets/utils/MemoryLruCache.ets rename to library/src/main/ets/cache/MemoryLruCache.ets diff --git a/library/src/main/ets/components/ImageKnifeAnimatorComponent.ets b/library/src/main/ets/components/ImageKnifeAnimatorComponent.ets index 2e1671f..93cdf84 100644 --- a/library/src/main/ets/components/ImageKnifeAnimatorComponent.ets +++ b/library/src/main/ets/components/ImageKnifeAnimatorComponent.ets @@ -12,25 +12,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AnimatorOption, ImageKnifeOption } from '../ImageKnifeOption'; -import { ImageKnifeRequest, ImageKnifeRequestState } from '../ImageKnifeRequest'; +import { AnimatorOption, ImageKnifeOption } from '../model/ImageKnifeOption'; +import { ImageKnifeRequest, ImageKnifeRequestState } from '../model/ImageKnifeRequest'; import common from '@ohos.app.ability.common'; import { ImageKnife } from '../ImageKnife'; import { LogUtil } from '../utils/LogUtil'; import { ImageKnifeRequestSource } from '../model/ImageKnifeData'; -@Component +@ComponentV2 export struct ImageKnifeAnimatorComponent { - @Watch('watchImageKnifeOption') @ObjectLink imageKnifeOption: ImageKnifeOption; - @Watch('watchAnimatorOption') @State animatorOption: AnimatorOption = new AnimatorOption(); - @State pixelMap: PixelMap | string | undefined = undefined - @State imageAnimator: Array | undefined = undefined - @State state: AnimationStatus = AnimationStatus.Running - @State iterations: number = -1 - @State reverse: boolean = false - @State adaptiveWidth: Length = '100%' - @State adaptiveHeight: Length = '100%' - @State objectFit: ImageFit = ImageFit.Contain + @Param animatorOption: AnimatorOption = new AnimatorOption(); + @Local pixelMap: PixelMap | string | undefined = undefined + @Local imageAnimator: Array | undefined = undefined + @Local adaptiveWidth: Length = '100%' + @Local adaptiveHeight: Length = '100%' + @Local objectFit: ImageFit = ImageFit.Contain private request: ImageKnifeRequest | undefined private lastWidth: number = 0 private lastHeight: number = 0 @@ -38,7 +34,17 @@ export struct ImageKnifeAnimatorComponent { private currentHeight: number = 0 private componentVersion: number = 0 private currentContext: common.UIAbilityContext | undefined = undefined - + @Param imageKnifeOption: ImageKnifeOption = new ImageKnifeOption(); + @Monitor('imageKnifeOption', + "imageKnifeOption.loadSrc","imageKnifeOption.signature","imageKnifeOption.transformation","imageKnifeOption.border","imageKnifeOption.objectFit") + watchImageKnifeOption() { + if (this.request !== undefined) { + this.request.requestState = ImageKnifeRequestState.DESTROY + } + this.request = undefined + this.componentVersion++ + ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight)) + } aboutToAppear(): void { this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit } @@ -64,9 +70,9 @@ export struct ImageKnifeAnimatorComponent { .width(this.adaptiveWidth) .height(this.adaptiveHeight) .border(this.imageKnifeOption.border) - .state(this.state) - .iterations(this.iterations) - .reverse(this.reverse) + .state(this.animatorOption.state == undefined ? AnimationStatus.Running : this.animatorOption.state) + .iterations(this.animatorOption.iterations == undefined ? -1 : this.animatorOption.iterations) + .reverse(this.animatorOption.reverse == undefined ? false : this.animatorOption.reverse) .onSizeChange((oldValue:SizeOptions, newValue:SizeOptions) => { this.currentWidth = newValue.width as number this.currentHeight = newValue.height as number @@ -82,27 +88,11 @@ export struct ImageKnifeAnimatorComponent { } } }) - } - - watchAnimatorOption(){ - if(this.animatorOption.state != undefined) { - this.state = this.animatorOption.state - } - if(this.animatorOption.iterations != undefined) { - this.iterations = this.animatorOption.iterations - } - if(this.animatorOption.reverse != undefined) { - this.reverse = this.animatorOption.reverse - } - } - - watchImageKnifeOption() { - if (this.request !== undefined) { - this.request.requestState = ImageKnifeRequestState.DESTROY - } - this.request = undefined - this.componentVersion++ - ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight),true) + .onStart(this.animatorOption.onStart) + .onFinish(this.animatorOption.onFinish) + .onPause(this.animatorOption.onPause) + .onCancel(this.animatorOption.onCancel) + .onRepeat(this.animatorOption.onRepeat) } getCurrentContext(): common.UIAbilityContext { diff --git a/library/src/main/ets/components/ImageKnifeComponent.ets b/library/src/main/ets/components/ImageKnifeComponent.ets index 57e7432..59812e9 100644 --- a/library/src/main/ets/components/ImageKnifeComponent.ets +++ b/library/src/main/ets/components/ImageKnifeComponent.ets @@ -12,8 +12,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeOption } from '../ImageKnifeOption'; -import { ImageKnifeRequest, ImageKnifeRequestState } from '../ImageKnifeRequest'; +import { ImageKnifeOption } from '../model/ImageKnifeOption'; +import { ImageKnifeRequest, ImageKnifeRequestState } from '../model/ImageKnifeRequest'; import common from '@ohos.app.ability.common'; import { ImageKnife } from '../ImageKnife'; import { LogUtil } from '../utils/LogUtil'; @@ -21,14 +21,13 @@ import { ImageKnifeData, ImageKnifeRequestSource } from '../model/ImageKnifeData import { IEngineKey } from '../key/IEngineKey'; import { DefaultEngineKey } from '../key/DefaultEngineKey'; -@Component +@ComponentV2 export struct ImageKnifeComponent { - @Watch('watchImageKnifeOption') @ObjectLink imageKnifeOption: ImageKnifeOption; - @State pixelMap: PixelMap | string | undefined = undefined - @State syncLoad: boolean = false - @State adaptiveWidth: Length = '100%' - @State adaptiveHeight: Length = '100%' - @State objectFit: ImageFit = ImageFit.Contain + @Local pixelMap: PixelMap | string | undefined = undefined + @Param syncLoad: boolean = false + @Local adaptiveWidth: Length = '100%' + @Local adaptiveHeight: Length = '100%' + @Local objectFit: ImageFit = ImageFit.Contain private request: ImageKnifeRequest | undefined private lastWidth: number = 0 private lastHeight: number = 0 @@ -36,43 +35,63 @@ export struct ImageKnifeComponent { private currentHeight: number = 0 private componentVersion: number = 0 private currentContext: common.UIAbilityContext | undefined = undefined + @Param imageKnifeOption: ImageKnifeOption = new ImageKnifeOption(); + + @Monitor('imageKnifeOption', + "imageKnifeOption.loadSrc","imageKnifeOption.signature","imageKnifeOption.transformation","imageKnifeOption.border","imageKnifeOption.objectFit",'imageKnifeOption.downsampleOf') + watchImageKnifeOption() { + this.clearLastRequest() + this.componentVersion++ + this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit + LogUtil.log("watchImageKnifeOption execute request:width=" + this.currentWidth + " height= " + this.currentHeight + + " loadSrc = " + this.request?.imageKnifeOption.loadSrc + + " placeholderSrc = " + this.request?.imageKnifeOption.placeholderSrc + + " errorholderSrc = " + this.request?.imageKnifeOption.errorholderSrc) + ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight)) + } aboutToAppear(): void { - //闪动问题失效,注释相应代码后续修复 - if(this.syncLoad) { + this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit + + if(this.syncLoad) { //针对部分消息列表最新消息的图片闪动问题,建议使用同步方式在aboutToAppear时加载图片 let engineKey: IEngineKey = new DefaultEngineKey(); let memoryCacheSrc: ImageKnifeData | undefined = ImageKnife.getInstance() .loadFromMemoryCache(engineKey.generateMemoryKey(this.imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,this.imageKnifeOption)) if (memoryCacheSrc !== undefined){ - LogUtil.log("aboutToAppear load from memory cache for key = "+ engineKey.generateMemoryKey(this.imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,this.imageKnifeOption)) - //画主图 + LogUtil.log("aboutToAppear success load loadSrc from memory cache for loadSrc = "+ this.imageKnifeOption.loadSrc) this.pixelMap = memoryCacheSrc.source; - }else { - let memoryCachePlace: ImageKnifeData | undefined = ImageKnife.getInstance() - .loadFromMemoryCache(engineKey.generateMemoryKey(this.imageKnifeOption.placeholderSrc!,ImageKnifeRequestSource.PLACE_HOLDER,this.imageKnifeOption)) - if (memoryCachePlace !== undefined){ - LogUtil.log("aboutToAppear load from memory cache for key = "+ engineKey.generateMemoryKey(this.imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,this.imageKnifeOption)) - //画主图 - this.pixelMap = memoryCachePlace.source; + }else{ + LogUtil.log("aboutToAppear fail load loadSrc from memory cache for loadSrc = "+ this.imageKnifeOption.loadSrc) + if (this.imageKnifeOption.placeholderSrc !== undefined){ + let memoryCachePlace: ImageKnifeData | undefined = ImageKnife.getInstance() + .loadFromMemoryCache(engineKey.generateMemoryKey(this.imageKnifeOption.placeholderSrc!,ImageKnifeRequestSource.PLACE_HOLDER,this.imageKnifeOption)) + if (memoryCachePlace !== undefined){ + LogUtil.log("aboutToAppear success load placeholderSrc from memory cache for placeholderSrc = " + this.imageKnifeOption.placeholderSrc) + this.pixelMap = memoryCachePlace.source; + }else{ + LogUtil.log("aboutToAppear fail load placeholderSrc from memory cache for placeholderSrc = " + this.imageKnifeOption.placeholderSrc) + } } } } - this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit } aboutToDisappear(): void { - if (this.request !== undefined) { - this.request.requestState = ImageKnifeRequestState.DESTROY - this.request = undefined - } + this.clearLastRequest() } aboutToRecycle() { + this.clearLastRequest() + } + + /** + * 对已DESTROY的组件不再发起请求 + */ + private clearLastRequest(){ if (this.request !== undefined) { this.request.requestState = ImageKnifeRequestState.DESTROY this.request = undefined } - this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit } build() { @@ -90,27 +109,19 @@ export struct ImageKnifeComponent { this.currentHeight = newValue.height as number this.lastWidth = oldValue.width as number this.lastHeight = oldValue.height as number - if (this.currentWidth <= 0 || this.currentHeight <= 0) { - // 存在宽或者高为0,此次重回无意义,无需进行request请求 - } else { - // 前提:宽高值均有效,值>0. 条件1:当前宽高与上一次宽高不同 条件2:当前是第一次绘制 - if (this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) { - LogUtil.log("execute request:width=" + this.currentWidth + " height= " + this.currentHeight) - ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight)) - } + + // 条件1: 宽高值均有效,值>0. 条件2:当前宽高与上一次宽高不同 + if (this.currentWidth > 0 && this.currentHeight > 0 && + (this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth)) { + LogUtil.log("onSizeChange execute request:width=" + this.currentWidth + " height= " + this.currentHeight + + " loadSrc = " + this.request?.imageKnifeOption.loadSrc + + " placeholderSrc = " + this.request?.imageKnifeOption.placeholderSrc + + " errorholderSrc = " + this.request?.imageKnifeOption.errorholderSrc) + ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight)) } }) } - watchImageKnifeOption() { - if (this.request !== undefined) { - this.request.requestState = ImageKnifeRequestState.DESTROY - } - this.request = undefined - this.componentVersion++ - ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight)) - } - getCurrentContext(): common.UIAbilityContext { if (this.currentContext == undefined) { this.currentContext = getContext(this) as common.UIAbilityContext @@ -132,24 +143,10 @@ export struct ImageKnifeComponent { return //针对reuse场景,不显示历史图片 } this.pixelMap = pixelMap - if (typeof this.pixelMap !== 'string') { - if (this.imageKnifeOption.objectFit === ImageFit.Auto) { - let info = await this.pixelMap.getImageInfo() - - this.adaptiveWidth = this.currentWidth - this.adaptiveHeight = info.size.height * this.currentWidth / info.size.width - - // if (this.currentWidth / this.currentHeight > info.size.width / info.size.height) { - // this.adaptiveWidth = this.currentWidth - // this.adaptiveHeight = info.size.height * this.currentWidth / this.currentHeight - // } - // else { - // this.adaptiveWidth = info.size.width * this.currentWidth / this.currentHeight - // this.adaptiveHeight = this.currentHeight - // } - } - } else { - //console.info("KKKKKKKKKKK:" + pixelMap) + if (typeof this.pixelMap !== 'string' && this.imageKnifeOption.objectFit === ImageFit.Auto) { //针对静态图高度自适应 + let info = await this.pixelMap.getImageInfo() + this.adaptiveWidth = this.currentWidth + this.adaptiveHeight = info.size.height * this.currentWidth / info.size.width } if (requestSource == ImageKnifeRequestSource.SRC) { @@ -168,8 +165,4 @@ export struct ImageKnifeComponent { return this.request } -} - -interface KeyCanvas { - keyId: string } \ No newline at end of file diff --git a/library/src/main/ets/downsampling/BaseDownsampling.ets b/library/src/main/ets/downsampling/BaseDownsampling.ets new file mode 100644 index 0000000..439a1cb --- /dev/null +++ b/library/src/main/ets/downsampling/BaseDownsampling.ets @@ -0,0 +1,23 @@ +/* + * 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 { DownsampleStrategy } from './DownsampleStartegy'; + +import { SampleSizeRounding } from './DownsampleUtils'; + +export interface BaseDownsampling { + getName(): string + + getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number,downsampType?:DownsampleStrategy): number +} \ No newline at end of file diff --git a/library/src/main/ets/downsampling/DownsampleStartegy.ets b/library/src/main/ets/downsampling/DownsampleStartegy.ets new file mode 100644 index 0000000..5e95a19 --- /dev/null +++ b/library/src/main/ets/downsampling/DownsampleStartegy.ets @@ -0,0 +1,148 @@ +/* + * 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 { BaseDownsampling } from './BaseDownsampling'; +import { getScale, highestOneBit, round, SampleSizeRounding } from './DownsampleUtils'; + +export class FitCenter implements BaseDownsampling { + getName() { + return "FitCenter" + } + + getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number, + downsampType: DownsampleStrategy + ): number { + //重新计算宽高比; + let outSize: Size = { + width: round(getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType) * sourceWidth), + height:round(getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType) * sourceHeight) + } + let scaleFactor = downsampType === DownsampleStrategy.FIT_CENTER_QUALITY? + Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height))) : + Math.max(1, highestOneBit(Math.min(sourceWidth / outSize.width, sourceHeight / outSize.height)))//将整型的缩放因子转换为2的次幂采样大小 + + if (downsampType === DownsampleStrategy.FIT_CENTER_MEMORY + && (scaleFactor < (1 / getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)))) { + scaleFactor = scaleFactor << 1; + } + return scaleFactor + } +} + + +/*宽高进行等比缩放宽高里面最小的比例先放进去 +然后再更据原图的缩放比去适配另一边*/ + +export class AtLeast implements BaseDownsampling { + getName() { + return "AtLeast" + } + + getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { + const widthPercentage = requestedWidth / sourceWidth; + const heightPercentage = requestedHeight / sourceHeight; + //返回宽度和高度比例中最大的值 + let outSize: Size = { + width: round(Math.max(widthPercentage, heightPercentage) * sourceWidth), + height:round(Math.max(widthPercentage, heightPercentage) * sourceHeight) + } + let scaleFactor = Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height))) + + return scaleFactor + } +} + +/*请求尺寸大于实际尺寸不进行放大,按照原图展示*/ + +export class AtMost implements BaseDownsampling { + getName() { + return "AtMost" + } + + + getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { + const maxIntegerFactor = Math.ceil(Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth)); + let lesserOrEqualSampleSize = Math.max(1, highestOneBit(maxIntegerFactor)); + let greaterOrEqualSampleSize = lesserOrEqualSampleSize + if (lesserOrEqualSampleSize < maxIntegerFactor) { + greaterOrEqualSampleSize = lesserOrEqualSampleSize <<= 1; + } + greaterOrEqualSampleSize = lesserOrEqualSampleSize << (lesserOrEqualSampleSize < maxIntegerFactor ? 1 : 0) + + let outSize: Size = { + width: round((1 / greaterOrEqualSampleSize) * sourceWidth), + height:round((1 / greaterOrEqualSampleSize) * sourceHeight) + } + let scaleFactor = Math.max(1, highestOneBit(Math.min(sourceWidth / outSize.width, sourceHeight / outSize.height))) + if ((scaleFactor < greaterOrEqualSampleSize)) { + scaleFactor = scaleFactor << 1; + } + return scaleFactor + } +} + +/*宽高进行等比缩放宽高里面最大的比例先放进去 +然后再更据原图的缩放比去适配另一边*/ +export class CenterInside implements BaseDownsampling { + getName() { + return "CenterInside" + } + getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number, + downsampType: DownsampleStrategy + ): number { + let outSize: Size = { + width: round(Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)) * sourceWidth), + height:round(Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)) * sourceHeight) + } + //将整型的缩放因子转换为2的次幂采样大小 + let scaleFactor = this.getSampleSizeType(sourceWidth, sourceHeight, requestedWidth, requestedHeight, + downsampType) == SampleSizeRounding.QUALITY ? + Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height))) : + Math.max(1, highestOneBit(Math.min(sourceWidth / outSize.width, sourceHeight / outSize.height))) + if (this.getSampleSizeType(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType) + == SampleSizeRounding.MEMORY && (scaleFactor < (1 / Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType))))) { + scaleFactor = scaleFactor << 1; + } + return scaleFactor + + } + + getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number, + downsampType: DownsampleStrategy + ): SampleSizeRounding { + //如果缩放因子为 1,表示没有缩放,优先选择质量 + if (Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)) === 1) { + return SampleSizeRounding.QUALITY + } + //否则,使用 FIL_CENTER 的 SampleSizeRounding 值 + return downsampType === DownsampleStrategy.CENTER_OUTSIDE_MEMORY?SampleSizeRounding.MEMORY:SampleSizeRounding.QUALITY + } +} + +export enum DownsampleStrategy { + //请求尺寸大于实际尺寸不进行放大 + AT_MOST, + //两边自适应内存优先 + FIT_CENTER_MEMORY, + //两边自适应质量优先 + FIT_CENTER_QUALITY, + //按照宽高比的最大比进行适配内存优先 + CENTER_OUTSIDE_MEMORY, + //按照宽高比的最大比进行适配质量优先 + CENTER_OUTSIDE_QUALITY, + //宽高进行等比缩放宽高里面最小的比例先放进去,然后再根据原图的缩放比去适配 + AT_LEAST, + //不进行降采样 + NONE, +} \ No newline at end of file diff --git a/library/src/main/ets/downsampling/DownsampleUtils.ets b/library/src/main/ets/downsampling/DownsampleUtils.ets new file mode 100644 index 0000000..567026a --- /dev/null +++ b/library/src/main/ets/downsampling/DownsampleUtils.ets @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { DownsampleStrategy } from './DownsampleStartegy'; + +export enum SampleSizeRounding { + /** + * Prefer to round the sample size up so that the image is downsampled to smaller than the + * requested size to use less memory. + */ + //(内存优先) + MEMORY, + /** + * Prefer to round the sample size down so that the image is downsampled to larger than the + * requested size to maintain quality at the expense of extra memory usage. + */ + //(质量优先) + QUALITY +} +//找出给定整数 i 中最高位的1(即最左边的1)所代表的值 +export function highestOneBit(i: number): number { + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + return i - (i >>> 1); +} + + +export function getScale(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number, + downsampType: DownsampleStrategy +): number { + if (downsampType === DownsampleStrategy.FIT_CENTER_MEMORY) { + const widthPercentage = requestedWidth / sourceWidth + const heightPercentage = requestedHeight / sourceHeight + return Math.min(widthPercentage, heightPercentage) + } else { + const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth); + return maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor); + + } +} +//四舍五入 +export function round(value: number): number { + return Math.floor(value + 0.5); +} \ No newline at end of file diff --git a/library/src/main/ets/downsampling/Downsampler.ets b/library/src/main/ets/downsampling/Downsampler.ets new file mode 100644 index 0000000..328e228 --- /dev/null +++ b/library/src/main/ets/downsampling/Downsampler.ets @@ -0,0 +1,73 @@ +/* + * 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 { + AtMost, + CenterInside, + AtLeast, + DownsampleStrategy, + FitCenter, +} from './DownsampleStartegy'; +export class Downsampler { + calculateScaling( + typeValue: string, + sourceWidth: number, //原始宽高 + sourceHeight: number, //原始宽高 + requestWidth: number, //请求宽高 + requestHeight: number, //请求宽高 + downsampType: DownsampleStrategy, + ): Size { + + if (sourceHeight <= 0 || sourceWidth <= 0) { + throw new Error(`Invalid width and height, sourceHeight:${sourceHeight}+ sourceWidth:${sourceWidth}`) + } + let downsampler = this.getDownsampler(downsampType); + let scaleFactor: number = + downsampler.getScaleFactor(sourceWidth, sourceHeight, requestWidth, requestHeight, downsampType);//缩放比 + //基于上一步得出的采样大小,根据不同的图片类型,计算采样后的图片尺寸 + if (typeValue === "png") { + return { + width: Math.floor(sourceWidth / scaleFactor), + height: Math.floor(sourceHeight / scaleFactor) + } + } else if (typeValue === "webp") { + return { + width: Math.round(sourceWidth / scaleFactor), + height: Math.round(sourceHeight / scaleFactor) + } + } else { + return { + width: sourceWidth / scaleFactor, + height: sourceHeight / scaleFactor + + } + } + } + getDownsampler(downsampType: DownsampleStrategy) { + switch (downsampType) { + case DownsampleStrategy.FIT_CENTER_MEMORY: + case DownsampleStrategy.FIT_CENTER_QUALITY: + return new FitCenter(); + case DownsampleStrategy.AT_MOST: + return new AtMost(); + case DownsampleStrategy.CENTER_OUTSIDE_MEMORY: + case DownsampleStrategy.CENTER_OUTSIDE_QUALITY: + return new CenterInside(); + case DownsampleStrategy.AT_LEAST: + return new AtLeast(); + default: + throw new Error('Unsupported downsampling strategy'); + } + } +} diff --git a/library/src/main/ets/key/DefaultEngineKey.ets b/library/src/main/ets/key/DefaultEngineKey.ets index 6d43be1..12f010f 100644 --- a/library/src/main/ets/key/DefaultEngineKey.ets +++ b/library/src/main/ets/key/DefaultEngineKey.ets @@ -13,10 +13,11 @@ * limitations under the License. */ import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5'; -import { ImageKnifeOption } from '../ImageKnifeOption'; +import { ImageKnifeOption } from '../model/ImageKnifeOption'; import { IEngineKey } from './IEngineKey'; import { PixelMapTransformation } from '../transform/PixelMapTransformation'; import { ImageKnifeRequestSource } from '../model/ImageKnifeData'; +import { DownsampleStrategy } from '../downsampling/DownsampleStartegy'; @Sendable export class DefaultEngineKey implements IEngineKey { @@ -31,6 +32,9 @@ export class DefaultEngineKey implements IEngineKey { if (imageKnifeOption.transformation) { key += "transformation=" + this.getTransformation(imageKnifeOption.transformation) + ";" } + if ((imageKnifeOption.downsampleOf !== DownsampleStrategy.NONE && imageKnifeOption.downsampleOf !== undefined)) { + key += "downsampleOf" + imageKnifeOption.downsampleOf +"width="+width+"height="+ height + } } return key } diff --git a/library/src/main/ets/key/IEngineKey.ets b/library/src/main/ets/key/IEngineKey.ets index 54be5bb..3f7a903 100644 --- a/library/src/main/ets/key/IEngineKey.ets +++ b/library/src/main/ets/key/IEngineKey.ets @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeOption } from '../ImageKnifeOption' +import { ImageKnifeOption } from '../model/ImageKnifeOption' import { ImageKnifeRequestSource } from '../model/ImageKnifeData' export interface IEngineKey { diff --git a/library/src/main/ets/model/ImageKnifeData.ets b/library/src/main/ets/model/ImageKnifeData.ets index 79fe5d0..8fb3b61 100644 --- a/library/src/main/ets/model/ImageKnifeData.ets +++ b/library/src/main/ets/model/ImageKnifeData.ets @@ -12,12 +12,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { HeaderOptions } from '../ImageKnifeOption' -import { ImageKnifeRequest } from '../ImageKnifeRequest' +import { HeaderOptions } from './ImageKnifeOption' +import { ImageKnifeRequest } from './ImageKnifeRequest' import { IEngineKey } from '../key/IEngineKey' import { PixelMapTransformation } from '../transform/PixelMapTransformation' import common from '@ohos.app.ability.common'; import { Size } from '@kit.ArkUI' +import { DownsampleStrategy } from '../downsampling/DownsampleStartegy' export interface ImageKnifeData { source: PixelMap | string, @@ -86,7 +87,7 @@ export interface RequestJobResult { */ export interface RequestJobRequest { context: common.UIAbilityContext, - src: string | PixelMap | Resource, + src: string | number, headers?: Array, allHeaders: Map, componentWidth: number, @@ -101,6 +102,11 @@ export interface RequestJobRequest { isWatchProgress: boolean memoryKey: string fileCacheFolder: string, - isAnimator?: boolean + isAnimator?: boolean, + moduleName?:string, + resName?: string, + targetWidth: number + targetHeight: number + downsampType: DownsampleStrategy } diff --git a/library/src/main/ets/model/ImageKnifeOption.ets b/library/src/main/ets/model/ImageKnifeOption.ets new file mode 100644 index 0000000..4e42b00 --- /dev/null +++ b/library/src/main/ets/model/ImageKnifeOption.ets @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import taskpool from '@ohos.taskpool'; +import common from '@ohos.app.ability.common' +import { CacheStrategy, ImageKnifeData,EventImage } from './ImageKnifeData'; +import { PixelMapTransformation } from '../transform/PixelMapTransformation'; +import { drawing } from '@kit.ArkGraphics2D'; +import { DownsampleStrategy } from '../downsampling/DownsampleStartegy'; + +export interface HeaderOptions { + key: string; + value: Object; +} +interface AnimatorType { + state?: AnimationStatus + iterations?: number + reverse?: boolean + onStart?:()=>void + onFinish?:()=>void + onPause?:()=>void + onCancel?:()=>void + onRepeat?:()=>void +} +@ObservedV2 +export class AnimatorOption { + @Trace + state?: AnimationStatus = AnimationStatus.Running + @Trace + iterations?: number = -1 + @Trace + reverse?: boolean = false + @Trace + onStart?:()=>void + @Trace + onFinish?:()=>void + @Trace + onPause?:()=>void + @Trace + onCancel?:()=>void + @Trace + onRepeat?:()=>void + constructor(option?:AnimatorType) { + this.state = option?.state + this.iterations = option?.iterations + this.reverse = option?.reverse + this.onStart = option?.onStart + this.onFinish = option?.onFinish + this.onPause = option?.onPause + this.onCancel = option?.onCancel + this.onRepeat = option?.onRepeat + } +} +interface ImageOption { + // 主图资源 + loadSrc: string | PixelMap | Resource + // 占位图 + placeholderSrc?: string | PixelMap | Resource + // 失败占位图 + errorholderSrc?: string | PixelMap | Resource + headerOption?: Array; + // 自定义缓存关键字 + signature?: string + // 主图填充效果 + objectFit?: ImageFit + // 占位图填充效果 + placeholderObjectFit?: ImageFit + // 错误图填充效果 + errorholderObjectFit?: ImageFit + customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise + border?: BorderOptions + // 缓存策略 + writeCacheStrategy?: CacheStrategy + // 仅使用缓存加载数据 + onlyRetrieveFromCache?: boolean; + priority?: taskpool.Priority + context?: common.UIAbilityContext; + progressListener?: (progress: number) => void; + transformation?: PixelMapTransformation + onLoadListener?: OnLoadCallBack | undefined; + onComplete?:(event:EventImage | undefined) => void + drawingColorFilter?: ColorFilter | drawing.ColorFilter + downsampleOf?: DownsampleStrategy +} +@ObservedV2 +export class ImageKnifeOption { + // 主图资源 + @Trace loadSrc: string | PixelMap | Resource = ""; + // 占位图 + placeholderSrc?: string | PixelMap | Resource; + // 失败占位图 + errorholderSrc?: string | PixelMap | Resource; + headerOption?: Array; + // 自定义缓存关键字 + @Trace signature?: string; + // 主图填充效果 + @Trace objectFit?: ImageFit + // 占位图填充效果 + placeholderObjectFit?: ImageFit + // 错误图填充效果 + errorholderObjectFit?: ImageFit + customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise + @Trace border?: BorderOptions + // 缓存策略 + writeCacheStrategy?: CacheStrategy + // 仅使用缓存加载数据 + onlyRetrieveFromCache?: boolean = false; + priority?: taskpool.Priority = taskpool.Priority.LOW + context?: common.UIAbilityContext; + progressListener?: (progress: number) => void; + @Trace transformation?: PixelMapTransformation + onLoadListener?: OnLoadCallBack | undefined; + onComplete?:(event:EventImage | undefined) => void + drawingColorFilter?: ColorFilter | drawing.ColorFilter + // 下采样 + @Trace downsampleOf: DownsampleStrategy = DownsampleStrategy.NONE + constructor(option?:ImageOption) { + this.loadSrc = option?.loadSrc == undefined ? "" : option?.loadSrc + this.placeholderSrc = option?.placeholderSrc + this.errorholderSrc = option?.errorholderSrc + this.headerOption = option?.headerOption + this.signature = option?.signature + this.objectFit = option?.objectFit + this.placeholderObjectFit = option?.placeholderObjectFit + this.errorholderObjectFit = option?.errorholderObjectFit + this.customGetImage = option?.customGetImage + this.border = option?.border + this.writeCacheStrategy = option?.writeCacheStrategy + this.onlyRetrieveFromCache = option?.onlyRetrieveFromCache + this.priority = option?.priority + this.context = option?.context + this.progressListener = option?.progressListener + this.transformation = option?.transformation + this.onLoadListener = option?.onLoadListener + this.onComplete = option?.onComplete + this.drawingColorFilter = option?.drawingColorFilter + this.downsampleOf = option?.downsampleOf==undefined?DownsampleStrategy.NONE:option?.downsampleOf + } +} + +/** + * 请求回调 + */ +export interface OnLoadCallBack { + // 请求开始 + onLoadStart?: () => void; + + // 请求成功 + onLoadSuccess?: (data: string | PixelMap | undefined, imageKnifeData: ImageKnifeData) => void; + + // 请求结束 + onLoadFailed?: (err: string) => void; + // 请求取消 + onLoadCancel?: (reason: string) => void; +} \ No newline at end of file diff --git a/library/src/main/ets/ImageKnifeRequest.ets b/library/src/main/ets/model/ImageKnifeRequest.ets similarity index 89% rename from library/src/main/ets/ImageKnifeRequest.ets rename to library/src/main/ets/model/ImageKnifeRequest.ets index cb2115b..530b707 100644 --- a/library/src/main/ets/ImageKnifeRequest.ets +++ b/library/src/main/ets/model/ImageKnifeRequest.ets @@ -14,7 +14,8 @@ */ import { ImageKnifeOption } from './ImageKnifeOption'; import common from '@ohos.app.ability.common'; -import { ImageKnifeRequestSource } from './model/ImageKnifeData'; +import { ImageKnifeRequestSource } from './ImageKnifeData'; +import { DownsampleStrategy } from '../downsampling/DownsampleStartegy'; export class ImageKnifeRequest { @@ -27,18 +28,22 @@ export class ImageKnifeRequest { ImageKnifeRequestCallback: ImageKnifeRequestCallback componentVersion: number = 0 headers: Map = new Map() + downsampType?: DownsampleStrategy constructor(option: ImageKnifeOption, uIAbilityContext: common.UIAbilityContext, width: number, height: number, version: number, - ImageKnifeRequestCallback: ImageKnifeRequestCallback) { + ImageKnifeRequestCallback: ImageKnifeRequestCallback, + downsampType?: DownsampleStrategy + ) { this.imageKnifeOption = option this.context = uIAbilityContext this.componentWidth = width this.componentHeight = height this.componentVersion = version this.ImageKnifeRequestCallback = ImageKnifeRequestCallback + this.downsampType = downsampType } // RequestOption调用header对于的方法 addHeader(key: string, value: Object) { diff --git a/library/src/main/ets/utils/DefaultJobQueue.ets b/library/src/main/ets/queue/DefaultJobQueue.ets similarity index 96% rename from library/src/main/ets/utils/DefaultJobQueue.ets rename to library/src/main/ets/queue/DefaultJobQueue.ets index ac31956..53f3d2a 100644 --- a/library/src/main/ets/utils/DefaultJobQueue.ets +++ b/library/src/main/ets/queue/DefaultJobQueue.ets @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeRequest } from '../ImageKnifeRequest'; +import { ImageKnifeRequest } from '../model/ImageKnifeRequest'; import { IJobQueue } from './IJobQueue' import Queue from '@ohos.util.Queue'; import { taskpool,Stack } from '@kit.ArkTS'; diff --git a/library/src/main/ets/utils/IJobQueue.ets b/library/src/main/ets/queue/IJobQueue.ets similarity index 93% rename from library/src/main/ets/utils/IJobQueue.ets rename to library/src/main/ets/queue/IJobQueue.ets index 0a51ff0..39a3923 100644 --- a/library/src/main/ets/utils/IJobQueue.ets +++ b/library/src/main/ets/queue/IJobQueue.ets @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeRequest } from '../ImageKnifeRequest' +import { ImageKnifeRequest } from '../model/ImageKnifeRequest' export interface IJobQueue { diff --git a/library/src/main/ets/model/utils.ets b/library/src/main/ets/utils/ArrayBufferUtils.ets similarity index 100% rename from library/src/main/ets/model/utils.ets rename to library/src/main/ets/utils/ArrayBufferUtils.ets diff --git a/library/src/main/ets/utils/LogUtil.ets b/library/src/main/ets/utils/LogUtil.ets index 02c2d39..d59f582 100644 --- a/library/src/main/ets/utils/LogUtil.ets +++ b/library/src/main/ets/utils/LogUtil.ets @@ -12,44 +12,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { hilog } from '@kit.PerformanceAnalysisKit'; + export class LogUtil { - public static OFF: number = 1 - public static LOG: number = 2 - public static DEBUG: number = 3 - public static INFO: number = 4 - public static WARN: number = 5 - public static ERROR: number = 6 - public static ALL: number = 7 - public static mLogLevel: number = LogUtil.OFF; - public static TAG: string = "ImageKnife:: "; + public static readonly DOMAIN: number = 0xD002220; + public static readonly TAG: string = "ImageKnife::"; public static debug(message: string, ...args: Object[]) { - if (LogUtil.mLogLevel >= LogUtil.DEBUG) { - console.debug(LogUtil.TAG + message, args) - } + hilog.debug(LogUtil.DOMAIN, LogUtil.TAG, message, args) } public static info(message: string, ...args: Object[]) { - if (LogUtil.mLogLevel >= LogUtil.INFO) { - console.info(LogUtil.TAG + message, args) - } + hilog.info(LogUtil.DOMAIN, LogUtil.TAG, message, args) } public static log(message: string, ...args: Object[]) { - if (LogUtil.mLogLevel >= LogUtil.LOG) { - console.log(LogUtil.TAG + message, args) - } + hilog.debug(LogUtil.DOMAIN, LogUtil.TAG, message, args) } public static warn(message: string, ...args: Object[]) { - if (LogUtil.mLogLevel >= LogUtil.WARN) { - console.warn(LogUtil.TAG + message, args) - } + hilog.warn(LogUtil.DOMAIN, LogUtil.TAG, message, args) } public static error(message: string, ...args: Object[]) { - if (LogUtil.mLogLevel >= LogUtil.ERROR) { - console.error(LogUtil.TAG + message, args) - } + hilog.error(LogUtil.DOMAIN, LogUtil.TAG, message, args) } } \ No newline at end of file diff --git a/library/src/main/ets/utils/Tools.ets b/library/src/main/ets/utils/Tools.ets deleted file mode 100644 index a51faa8..0000000 --- a/library/src/main/ets/utils/Tools.ets +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2024 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5' -import util from '@ohos.util' - -export class Tools { - private static keyCache: util.LRUCache = new util.LRUCache(1024) - public static generateMemoryKey(key: string | PixelMap | Resource): string{ - return typeof key == "string"? key : JSON.stringify(key) - } - // 生成唯一的key - public static generateKey(key: string | PixelMap | Resource): string{ - let keyCache = typeof key == "string"? key : JSON.stringify(key) - let result = Tools.keyCache.get(keyCache) - if(result != undefined) { - return result - } else { - result = SparkMD5.hashBinary(keyCache) - Tools.keyCache.put(keyCache,result) - return result - } - } -} \ No newline at end of file diff --git a/library/src/main/resources/base/media/rabbit.gif b/library/src/main/resources/base/media/rabbit.gif deleted file mode 100644 index c2c1402..0000000 Binary files a/library/src/main/resources/base/media/rabbit.gif and /dev/null differ diff --git a/oh-package.json5 b/oh-package.json5 index 34e1875..79b1475 100644 --- a/oh-package.json5 +++ b/oh-package.json5 @@ -1,16 +1,11 @@ { - "modelVersion": "5.0.0", - "name": "imageknife", - "version": "1.0.0", - "description": "Please describe the basic information.", - "main": "", - "author": "", - "license": "", - "dependencies": { - "@ohos/gpu_transform": "^1.0.2" - }, + "license": "ISC", "devDependencies": { - "@ohos/hypium": "1.0.16" + "@ohos/hypium": "1.0.6" }, - "dynamicDependencies": {} -} \ No newline at end of file + "name": "imageknife", + "description": "example description", + "repository": {}, + "version": "", + "dependencies": {} +} diff --git a/sharedlibrary/src/main/ets/pages/Index.ets b/sharedlibrary/src/main/ets/pages/Index.ets index ad35007..92d223f 100644 --- a/sharedlibrary/src/main/ets/pages/Index.ets +++ b/sharedlibrary/src/main/ets/pages/Index.ets @@ -14,14 +14,14 @@ */ import { ImageKnife , ImageKnifeComponent ,ImageKnifeOption } from "@ohos/imageknife" -@Component +@ComponentV2 export struct IndexComponent { - @State imageKnifeOption: ImageKnifeOption = { + @Local imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({ loadSrc: $r('app.media.startIcon') - } + }) build() { Column() { - Button("预加载").onClick((event: ClickEvent) => { + Button($r('app.string.Preload')).onClick((event: ClickEvent) => { ImageKnife.getInstance() .preLoadCache('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp') .then((data) => { diff --git a/sharedlibrary/src/main/resources/base/element/string.json b/sharedlibrary/src/main/resources/base/element/string.json index 98e1d8a..dc2fa1f 100644 --- a/sharedlibrary/src/main/resources/base/element/string.json +++ b/sharedlibrary/src/main/resources/base/element/string.json @@ -3,6 +3,10 @@ { "name": "shared_desc", "value": "description" + }, + { + "name": "Preload", + "value": "Preload" } ] } \ No newline at end of file diff --git a/sharedlibrary/src/main/resources/zh_CN/element/string.json b/sharedlibrary/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000..718792d --- /dev/null +++ b/sharedlibrary/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "Preload", + "value": "预加载" + } + ] +} \ No newline at end of file