# Conflicts:
#	entry/src/main/ets/pages/manyPhotoShowPage.ets
#	imageknife/src/main/ets/components/imageknife/ImageKnife.ets
#	imageknife/src/main/ets/components/imageknife/ImageKnifeComponent.ets
#	imageknife/src/main/ets/components/imageknife/RequestOption.ets
#	imageknife/src/main/ets/components/imageknife/networkmanage/DownloadClient.ets
#	imageknife/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets
#	imageknife/src/main/ets/components/imageknife/utils/FileTypeUtil.ets
#	imageknife/src/main/ets/components/imageknife/utils/base/EasyLinkedHashMap.ets
This commit is contained in:
zhoulisheng 2023-11-16 14:49:15 +08:00
commit 8ea608a83a
56 changed files with 655 additions and 328 deletions

View File

@ -3,7 +3,7 @@
"bundleName": "com.openharmony.imageknife",
"vendor": "example",
"versionCode": 1000000,
"versionName": "2.1.1-rc.0",
"versionName": "2.1.1-rc.3",
"icon": "$media:app_icon",
"label": "$string:app_name",
"distributedNotificationEnabled": true

View File

@ -1,6 +1,18 @@
## 2.1.1-rc.3
- 门面类ImageKnife新增pauseRequests接口,全局暂停请求
- 门面类ImageKnife新增resumeRequests接口,全局恢复暂停
## 2.1.1-rc.2
- gif解码改为imageSource解码,不在对worker强依赖
- 下载接口修改为http.requestInStream
## 2.1.1-rc.1
- 新增自定义key参数配置
- 新增MemoryLruCache主动调用PixelMap的release方法,释放native的PixelMap内存
- 新增ImageSource主动调用release方法释放native持有的ImageSource内存
## 2.1.1-rc.0
@ -8,7 +20,7 @@
## 2.1.0
- ArkTs语法整改:
- ArkTs语法适配:
globalThis.ImageKnife方式已经不可使用

View File

@ -37,7 +37,7 @@ ohpm install @ohos/imageknife
"author": "",
"license": "",
"dependencies": {
"@ohos/imageknife": "^2.0.6"
"@ohos/imageknife": "^2.1.1-rc.3"
}
}
```
@ -462,15 +462,17 @@ request.skipMemoryCache(true)
| skipMemoryCache(skip: boolean) | skip: boolean | 配置是否跳过内存缓存 |
| retrieveDataFromCache(flag: boolean) | flag: boolean | 配置仅从缓存中加载数据 |
| signature | ObjectKey | 自定义key |
同时支持[图片变换相关](#图片变换相关)接口。
### ImageKnife 启动器/门面类
| 方法名 | 入参 | 接口描述 |
|---------------------------------|------------------------|-------------------|
| call(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行加载流程 |
| 方法名 | 入参 | 接口描述 |
| ------------------------------- | ---------------------- | ---------------------------------- |
| call(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行加载流程 |
| preload(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行预加载流程 |
| pauseRequests() | | 全局暂停请求 |
| resumeRequests() | | 全局恢复暂停 |
### 缓存策略相关

View File

@ -4,7 +4,7 @@
"name": "entry",
"description": "example description",
"repository": {},
"version": "2.1.1-rc.0",
"version": "2.1.1-rc.3",
"dependencies": {
"@ohos/imageknife": "file:../imageknife",
"@ohos/disklrucache": "^2.0.2-rc.0"

View File

@ -13,7 +13,7 @@
* limitations under the License.
*/
import { EngineKeyFactories, EngineKeyInterface, RequestOption } from '@ohos/imageknife'
import { ObjectKey } from '@ohos/imageknife/src/main/ets/components/imageknife/ObjectKey';
import { ObjectKey } from '@ohos/imageknife';
export class CustomEngineKeyImpl implements EngineKeyInterface {
redefineUrl: (loadSrc: string) => string;

View File

@ -23,8 +23,10 @@ struct basicTestFileIOPage {
@State filePath: string = '查看featureAbility路径';
appFilePath = '';
appCachePath = '';
@State imageHint: string = ''
@State imageFile: string = '文字提醒'
@State imageHint: string = '文字提醒1'
@State imageHint2: string = '文字提醒2'
@State imageFile: string = ''
@State imageRes: Resource = $r('app.media.pngSample')
@State imagePixelMap?: PixelMap = undefined
@State normalPixelMap: boolean = false;
@ -63,7 +65,7 @@ struct basicTestFileIOPage {
.margin({ top: 10 })
.onClick(() => {
if(this.appFilePath == '' || this.appFilePath == null){
this.appFilePath = 'appFilePath未取到值,请按顺序从上往下,从左往右依次测试'
this.imageHint = 'appFilePath未取到值,请按顺序从上往下,从左往右依次测试'
return
}
console.log('files目录创建Folder1和Folder2 验证statSync mkdirSync')
@ -94,12 +96,13 @@ struct basicTestFileIOPage {
console.log('basicTestFileIOPage - 本地加载资源err' + JSON.stringify(err as BusinessError));
})
})
Text(this.imageHint2)
Button('copy:Folder1至Folder2 验证copyFileSync')
.margin({ top: 10 })
.onClick(() => {
console.log('copy:Folder1至Folder2 验证copyFileSync')
if(this.appFilePath == '' || this.appFilePath == null){
this.appFilePath = 'appFilePath未取到值,请按顺序从上往下,从左往右依次测试'
this.imageHint2 = 'appFilePath未取到值,请按顺序从上往下,从左往右依次测试'
return
}
let filePath1 = this.appFilePath + '/Folder1/jpgSample.gif';

View File

@ -41,7 +41,9 @@ struct BasicTestResourceManagerPage {
let arrayBuffer = this.typedArrayToBuffer(data);
let filetypeUtil = new FileTypeUtil();
let fileType = filetypeUtil.getFileType(arrayBuffer);
this.fileTypeStr = fileType;
if(fileType != null) {
this.fileTypeStr = fileType;
}
})
.catch((err:BusinessError) => {
console.log('basicTestFileIOPage - 本地加载资源err' + JSON.stringify(err));
@ -63,7 +65,9 @@ struct BasicTestResourceManagerPage {
.decode(data);
let filetypeUtil = new FileTypeUtil();
let fileType = filetypeUtil.getFileType(arrayBuffer);
this.fileTypeStr = fileType;
if(fileType != null) {
this.fileTypeStr = fileType;
}
})
.catch((err:BusinessError) => {
console.log('basicTestFileIOPage - 本地加载资源err' + JSON.stringify(err));

View File

@ -220,6 +220,11 @@ struct IndexFunctionDemo {
console.log("pages/manyPhotoShowPage 页面跳转")
router.pushUrl({ url: "pages/manyPhotoShowPage" });
}).margin({ top: 15 })
Button("图片加载暂停和恢复")
.onClick(() => {
console.log("pages/manyPhotoShowPage 页面跳转")
router.pushUrl({ url: "pages/photosPausedResumedPage" });
}).margin({ top: 15 })
Button("部分url测试")
.onClick(() => {
console.log("pages/tempUrlTestPage 页面跳转")
@ -248,6 +253,17 @@ struct IndexFunctionDemo {
router.pushUrl({ url: "pages/testGifLoadWithWorkerPage" });
}).margin({ top: 5, left: 3 })
}.width('100%').height(60).backgroundColor(Color.Pink)
Text("测试图片加载稳定").fontSize(15)
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button("测试多张网络图片加载速度")
.onClick(() => {
router.pushUrl({ url: "pages/testManyNetImageLoadWithPage" });
}).margin({ top: 5, left: 3 })
Button("测试多张gif加载位置")
.onClick(() => {
router.pushUrl({ url: "pages/testManyGifLoadWithPage" });
}).margin({ top: 5, left: 3 })
}.width('100%').height(60).backgroundColor(Color.Pink)
}
}
.width('100%')

View File

@ -31,36 +31,6 @@ struct ManyPhotoShowPage {
build() {
Column() {
Button('点击暂停加载')
.margin({top:10,bottom:5})
.onClick(()=>{
let imageKnife = ImageKnifeGlobal.getInstance().getImageKnife();
if(imageKnife!= undefined){
imageKnife.pauseRequests()
}
})
Button('点击重新加载')
.margin({top:10,bottom:5})
.onClick(()=>{
let imageKnife = ImageKnifeGlobal.getInstance().getImageKnife();
if(imageKnife!= undefined){
imageKnife.resumeRequests()
}
})
Button('设置磁盘存储为50M')
.margin({top:10,bottom:5})
.onClick(()=>{
if(ImageKnifeGlobal.getInstance().getImageKnife() != undefined) {
let disk: DiskLruCache | undefined = (ImageKnifeGlobal.getInstance().getImageKnife())?.getDiskMemoryCache();
if(disk != undefined) {
disk.setMaxSize(50 * 1024 * 1024)
Prompt.showToast({ message: "设置成功" })
}
}
})
List({ space: 20, scroller: this.elementScroller }) {
LazyForEach(this.data, (item: Material, index) => {
ListItem() {

View File

@ -0,0 +1,93 @@
/*
* Copyright (C) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ImageKnifeComponent, ScaleType} from '@ohos/imageknife'
import {ImageKnifeOption} from '@ohos/imageknife'
import {ImageKnifeGlobal} from '@ohos/imageknife'
import {RotateImageTransformation} from '@ohos/imageknife'
import {Material} from './model/Material'
import {TestDataSource} from './model/TestDataSource'
import {DiskLruCache} from '@ohos/disklrucache'
import ArkWorker from '@ohos.worker'
import Prompt from '@system.prompt'
@Entry
@Component
struct photosPausedResumedPage {
private data: TestDataSource = new TestDataSource();
private elementScroller: Scroller = new Scroller()
build() {
Column() {
Button('点击暂停加载')
.margin({top:10,bottom:5})
.onClick(()=>{
let imageKnife = ImageKnifeGlobal.getInstance().getImageKnife();
if(imageKnife!= undefined){
imageKnife.pauseRequests()
}
})
Button('点击重新加载')
.margin({top:10,bottom:5})
.onClick(()=>{
let imageKnife = ImageKnifeGlobal.getInstance().getImageKnife();
if(imageKnife!= undefined){
imageKnife.resumeRequests()
}
})
List({ space: 20, scroller: this.elementScroller }) {
LazyForEach(this.data, (item: Material, index) => {
ListItem() {
Column() {
Stack({ alignContent: Alignment.BottomEnd }) {
// 滤镜图片
ImageKnifeComponent({ imageKnifeOption: {
loadSrc: item.thumbnail,
mainScaleType: ScaleType.FIT_XY,
} })
}
.width(56).height(56)
//滤镜标题
Text(item.name)
.fontSize(10)
.maxLines(1)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
.layoutWeight(1)
.width('100%')
.backgroundColor(Color.Orange)
}
.width(56)
.height(72)
.clip(true)
.borderRadius(4)
}
}, (item: Material) => item.material_id)
}
.listDirection(Axis.Horizontal)
.width('100%')
.height(72)
}
}
}

View File

@ -120,7 +120,8 @@ struct PngjTestCasePage {
name: 'readPngImageAsync'
})
pngj.readPngImageAsync(png_worker, this.pngSource2!, {pngCallback: (sender:ArrayBuffer, value:Record<string,Object>) => {
this.pngSource1 = sender
this.pngSource2 = sender
this.hint8 = '重新获取buffer才能测试'
this.hint2 = 'img with=' + value.width + ' img height=' + value.height
+ ' img depth=' + value.depth + ' img ctype=' + value.ctype
this.pngdecodeRun2 = false;

View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ImageKnife, ImageKnifeComponent, ImageKnifeGlobal, ImageKnifeOption } from "@ohos/imageknife"
import worker from '@ohos.worker';
let gifUrl = "https://gw.alicdn.com/tfs/TB1E3H5t8Bh1e4jSZFhXXcC9VXa-198-198.gif"
let data: string[] = [
'https://media.giphy.com/media/hVgagDPf1IRFK/giphy.gif',
'https://placehold.co/600x400/000000/FFFFFF/png',
'https://s1.aigei.com/src/img/gif/92/922f58ca46c34b3e9947ddd4dc17ec32.gif?imageMogr2/auto-orient/thumbnail/!282x282r/gravity/Center/crop/282x282/quality/85/&e=1735488000&token=P7S2Xpzfz11vAkASLTkfHN7Fw-oOZBecqeJaxypL:YRYJJynbOC0Z_Nl7HunjuRr4-Vk=',
'https://placehold.co/600x400/000000/orange/png',
'https://s1.aigei.com/src/img/gif/6c/6c907924ef1546d3a593fae3e78b97f6.gif?imageMogr2/auto-orient/thumbnail/!282x282r/gravity/Center/crop/282x282/quality/85/&e=1735488000&token=P7S2Xpzfz11vAkASLTkfHN7Fw-oOZBecqeJaxypL:PozGIimx0mj5m69DQ0Z6qWn7mA0=',
'https://placehold.co/600x400/000000/orange/png?text=Hello+World'
]
@Entry
@Component
struct TestManyGifLoadWithPage {
private globalGifWorker?: worker.ThreadWorker = undefined
@State p1: PixelMap | undefined = undefined
@State p2: PixelMap | undefined = undefined
@State workerOption: ImageKnifeOption = {
loadSrc: $r('app.media.icon'),
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
};
build() {
Column() {
Text('gif Demo').align(Alignment.Center).fontSize(25).margin(10).fontWeight(FontWeight.Bolder)
Row() {
Column() {
Button('use Origin Image').align(Alignment.Center).fontSize(10).margin(2)
Image(gifUrl).width('100%').height(100).backgroundColor(Color.Blue).objectFit(ImageFit.Contain)
}.width('50%').backgroundColor(Color.Orange)
Column() {
Button('use ImageKnifeComponent').align(Alignment.Center).fontSize(10).margin(2)
ImageKnifeComponent({ imageKnifeOption: { loadSrc: gifUrl } })
.width('100%')
.height(100)
.backgroundColor(Color.Orange)
}.width('50%').backgroundColor(Color.Blue)
}
Row() {
Column() {
Button('use Worker').align(Alignment.Center).fontSize(10).margin(2)
.onClick(() => {
this.workerOption = {
loadSrc: gifUrl,
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
}
})
ImageKnifeComponent({ imageKnifeOption: this.workerOption })
.width('100%')
.height(100)
.backgroundColor(Color.Blue)
}.width('50%').backgroundColor(Color.Orange)
Column() {
Button('logs').align(Alignment.Center).fontSize(10).margin(2)
Text('logs').width('100%').height(100).backgroundColor(Color.Orange)
}.width('50%').backgroundColor(Color.Blue)
}
Grid() {
ForEach(data, (url: string) => {
GridItem(){
ImageKnifeComponent({imageKnifeOption:{
loadSrc:url
}}).backgroundColor(0x38393D).width(150).height(100)
}
})
}.rowsGap(2)
.columnsGap(2)
}.width('100%').height('100%').backgroundColor(0xF1F3F5)
}
aboutToAppear(){
this.globalGifWorker = new worker.ThreadWorker('entry/ets/workers/GifLoadWorker.ts',{
type:'classic',
name:'ImageKnifeParseGIF'
})
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife()
if(imageKnife != undefined){
imageKnife?.setGifWorker(this.globalGifWorker)
}
}
aboutToDisappear(){
if(this.globalGifWorker){
this.globalGifWorker.terminate()
}
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ScaleType, ImageKnifeComponent } from "@ohos/imageknife"
class CommonDataSource <T> implements IDataSource {
private dataArray: T[] = []
private listeners: DataChangeListener[] = []
constructor(element: []) {
this.dataArray = element
}
public getData(index: number) {
return this.dataArray[index]
}
public totalCount(): number {
return this.dataArray.length
}
public addData(index: number, data: T[]): void {
this.dataArray = this.dataArray.concat(data)
this.notifyDataAdd(index)
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener)
}
}
notifyDataAdd(index: number): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataAdd(index)
})
}
}
@Entry
@Component
struct TestManyNetImageLoadWithPage {
@State hotCommendList:CommonDataSource<string> = new CommonDataSource<string>([])
private data:Array<string> = [
'http://s.yingshidq.com.cn/cover/longbms/2023/08/11/2159934215_1248_702.jpeg',
'http://s.yingshidq.com.cn/imags/poster/2022/08/02/165937334218556809.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/08/10/4350315060_640_360.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/08/10/3835072893_1248_702.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/08/10/2821936016_1248_702.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/08/11/1311714870_1248_702.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/08/10/4421772097_1248_702.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/09/05/5898334347_400_225.png',
'http://s.yingshidq.com.cn/imags/poster/2022/12/06/167031399911862707.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/08/11/1405851829_1248_702.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/08/10/3796501624_1248_702.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/08/10/4202181519_1248_702.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/08/11/1449894622_1248_702.jpeg',
'http://s.yingshidq.com.cn/cover/longbms/2023/08/10/3756558151_1248_702.jpeg'
]
aboutToAppear() {
this.hotCommendList.addData(this.hotCommendList.totalCount(),this.data)
}
build() {
Scroll() {
Column() {
Grid() {
LazyForEach(this.hotCommendList, (item: string) => {
GridItem() {
ImageKnifeComponent({
imageKnifeOption: {
loadSrc: item,
placeholderSrc: $r('app.media.icon_loading'),
mainScaleType: ScaleType.CENTER_CROP,
placeholderScaleType: ScaleType.CENTER_CROP
}
}).width('100%').height('100%')
}.width('45%').height(200)
}, (item: string) => JSON.stringify(item))
}
.columnsTemplate('1fr 1fr')
.columnsGap(8)
.rowsGap(10)
.width('100%')
.hitTestBehavior(HitTestMode.None)
.maxCount(10)
}.margin({ top: 5 })
}
}
}

View File

@ -17,7 +17,7 @@ import { CropCircleTransformation } from '@ohos/imageknife'
import { RoundedCornersTransformation } from '@ohos/imageknife'
import {
CropCircleWithBorderTransformation
} from '@ohos/imageknife/src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation'
} from '@ohos/imageknife'
import { RotateImageTransformation } from '@ohos/imageknife'
import { CropSquareTransformation } from '@ohos/imageknife'
import { CropTransformation } from '@ohos/imageknife'

View File

@ -26,11 +26,14 @@
"pages/imageknifeTestCaseIndex",
"pages/dataShareUriLoadPage",
"pages/manyPhotoShowPage",
"pages/photosPausedResumedPage",
"pages/tempUrlTestPage",
"pages/drawFactoryTestPage",
"pages/testSingleFrameGifPage",
"pages/testGifLoadWithWorkerPage",
"pages/OptionTestPage",
"pages/SignatureTestPage"
"pages/SignatureTestPage",
"pages/testManyNetImageLoadWithPage",
"pages/testManyGifLoadWithPage"
]
}

View File

@ -95,6 +95,7 @@ export { UPNG } from './src/main/ets/components/3rd_party/upng/UPNG'
*/
export { ImageKnife } from './src/main/ets/components/imageknife/ImageKnife'
export { ImageKnifeGlobal } from './src/main/ets/components/imageknife/ImageKnifeGlobal'
export { ObjectKey } from './src/main/ets/components/imageknife/ObjectKey'
export {RequestOption,Size} from './src/main/ets/components/imageknife/RequestOption'
export { ImageKnifeComponent, ScaleType, ScaleTypeHelper } from './src/main/ets/components/imageknife/ImageKnifeComponent'
export { ImageKnifeDrawFactory } from './src/main/ets/components/imageknife/ImageKnifeDrawFactory'

View File

@ -14,7 +14,7 @@
"main": "index.ets",
"repository": "https://gitee.com/openharmony-tpc/ImageKnife",
"type": "module",
"version": "2.1.1-rc.1",
"version": "2.1.1-rc.3",
"dependencies": {
"@ohos/disklrucache": "^2.0.2-rc.0",
"@ohos/svg": "^2.1.1-rc.0",

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http:// www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ImageKnife } from '../imageknife/ImageKnife';
import { ImageKnifeData } from '../imageknife/ImageKnifeData';
import { LruCache } from './LruCache';
export class MemoryLruCache extends LruCache<string, ImageKnifeData>{
constructor(maxsize:number) {
super(maxsize)
}
// 移除较少使用的缓存数据
trimToSize(tempsize: number) {
while (true) {
if (tempsize < 0) {
this.map.clear()
this.size = 0
break
}
if (this.size <= tempsize || this.map.isEmpty()) {
break
}
let delkey = this.map.getFirstKey()
let data : ImageKnifeData|undefined = this.map.get(delkey)
if(data != undefined){
data.release()
}
this.map.remove(delkey)
this.size--
}
}
}

View File

@ -17,11 +17,11 @@ import { RequestOption } from '../../imageknife/RequestOption'
export interface EngineKeyInterface {
// 生成内存缓存
generateMemoryCacheKey(loadSrc: string, size: string, transformed: string, dontAnimate: boolean, signature: ObjectKey): string
generateMemoryCacheKey(loadSrc: string, size: string, transformed: string, dontAnimate: boolean, signature: ObjectKey | undefined): string
// 生成原图变换后的图片的磁盘缓存
generateTransformedDiskCacheKey(loadSrc: string, size: string, transformed: string, dontAnimate: boolean, signature: ObjectKey): string
generateTransformedDiskCacheKey(loadSrc: string, size: string, transformed: string, dontAnimate: boolean, signature: ObjectKey | undefined): string
// 生成原图的磁盘缓存
generateOriginalDiskCacheKey(loadSrc: string, signature: ObjectKey): string
generateOriginalDiskCacheKey(loadSrc: string, signature: ObjectKey | undefined): string
}

View File

@ -14,7 +14,6 @@
*/
import { DiskLruCache } from "@ohos/disklrucache"
import { LruCache } from "../cache/LruCache"
import { EngineKeyFactories } from "../cache/key/EngineKeyFactories"
import { EngineKeyInterface } from "../cache/key/EngineKeyInterface"
import { RequestOption } from "../imageknife/RequestOption"
@ -41,10 +40,11 @@ import worker from '@ohos.worker'
import common from '@ohos.app.ability.common'
import HashMap from '@ohos.util.HashMap'
import LinkedList from '@ohos.util.LinkedList'
import { MemoryLruCache } from '../cache/MemoryLruCache'
export class ImageKnife {
static readonly SEPARATOR: string = '/'
memoryCache: LruCache<string, ImageKnifeData>;
memoryCache: MemoryLruCache;
diskMemoryCache: DiskLruCache;
dataFetch: IDataFetch;
resourceFetch: IResourceFetch<ArrayBuffer>;
@ -78,7 +78,7 @@ export class ImageKnife {
this.pausedMaps = new EasyLinkedHashMap();
// 构造方法传入size 为保存文件个数
this.memoryCache = new LruCache<string, ImageKnifeData>(100);
this.memoryCache = new MemoryLruCache(100);
// 创建disk缓存 传入的size 为多少比特 比如20KB 传入20*1024
this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext());
@ -100,7 +100,7 @@ export class ImageKnife {
}
getMemoryCache(): LruCache<string, ImageKnifeData> {
getMemoryCache(): MemoryLruCache {
return this.memoryCache;
}
@ -136,7 +136,7 @@ export class ImageKnife {
return ImageKnifeGlobal.getInstance().getHapContext();
}
setMemoryCache(lrucache: LruCache<string, ImageKnifeData>) {
setMemoryCache(lrucache: MemoryLruCache) {
this.memoryCache = lrucache;
}
@ -177,9 +177,9 @@ export class ImageKnife {
// 替代原来的LruCache
public replaceLruCache(size: number) {
if (this.memoryCache.map.size() <= 0) {
this.memoryCache = new LruCache<string, ImageKnifeData>(size);
this.memoryCache = new MemoryLruCache(size);
} else {
let newLruCache = new LruCache<string, ImageKnifeData>(size);
let newLruCache = new MemoryLruCache(size);
this.memoryCache.foreachLruCache((value: ImageKnifeData, key: string, map: Object) => {
newLruCache.put(key, value);
})
@ -218,7 +218,7 @@ export class ImageKnife {
// 将未删除的所有request [run pend] 放入 [pause]
this.pausedMaps.clear()
console.log('dodo pauseRequests start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
LogUtil.log('dodo pauseRequests start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
// 将run存入pause
let runNode = this.runningMaps.getHead();
@ -228,7 +228,7 @@ export class ImageKnife {
runNode = runNode.next;
}
this.runningMaps.clear();
console.log('dodo pauseRequests start2 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
LogUtil.log('dodo pauseRequests start2 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
let pendNode = this.pendingMaps.getHead();
while (pendNode) {
@ -237,15 +237,15 @@ export class ImageKnife {
pendNode = pendNode.next
}
this.pendingMaps.clear()
console.log('dodo pauseRequests start3 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
LogUtil.log('dodo pauseRequests start3 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
})
}
// 恢复所有被暂停的请求
async resumeRequests(): Promise<void> {
await this.mutex.lock(async () => {
this.isPaused = false;
console.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
LogUtil.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
// 重启了之后需要把paused 里面的所有request重新发送
let headNode = this.pausedMaps.getHead();
while (headNode) {
@ -254,7 +254,7 @@ export class ImageKnife {
headNode = headNode.next
}
this.pausedMaps.clear()
console.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
LogUtil.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
})
}
@ -331,7 +331,7 @@ export class ImageKnife {
let signature = request.signature;
if (signature) {
if (signature != undefined) {
console.log("唯一标识:" + signature.getKey())
}
@ -421,7 +421,9 @@ export class ImageKnife {
}
if (hasEqualRunning) {
this.keyEqualPendingToRun(tailNode.value);
if(tailNode != null && tailNode.value != null) {
this.keyEqualPendingToRun(tailNode.value);
}
} else {
this.searchNextKeyToRun();
}

View File

@ -82,26 +82,30 @@ export struct ImageKnifeComponent {
private onReadyNext?: (data:ImageKnifeData|number|undefined) => void = undefined
private onReadyNextData:ImageKnifeData|number|undefined = undefined
private detachFromLayout:DetachFromLayout;
private detachFromLayout:DetachFromLayout|undefined = undefined;
build() {
Canvas(this.context)
.width('100%')
.height('100%')
.onAreaChange((oldValue: Area, newValue: Area) => {
this.currentWidth = newValue.width as number
this.currentHeight = newValue.height as number
if (this.currentWidth <= 0 || this.currentHeight <= 0) {
// 存在宽或者高为0,此次重回无意义,无需进行request请求
} else {
// 前提:宽高值均有效,值>0. 条件1当前宽高与上一次宽高不同 条件2:当前是第一次绘制
if ((this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) || this.firstDrawFlag) {
this.firstDrawFlag = false;
LogUtil.log('ImageKnifeComponent onAreaChange And Next To Execute. Canvas currentWidth =' + this.currentWidth + ' currentHeight=' + this.currentHeight)
this.lastWidth = this.currentWidth
this.lastHeight = this.currentHeight
this.imageKnifeExecute()
if(newValue != undefined && newValue.width != undefined && newValue.height != undefined) {
this.currentWidth = newValue.width as number
this.currentHeight = newValue.height as number
if (this.currentWidth <= 0 || this.currentHeight <= 0) {
// 存在宽或者高为0,此次重回无意义,无需进行request请求
} else {
// 前提:宽高值均有效,值>0. 条件1当前宽高与上一次宽高不同 条件2:当前是第一次绘制
if ((this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) || this.firstDrawFlag) {
this.firstDrawFlag = false;
LogUtil.log('ImageKnifeComponent onAreaChange And Next To Execute. Canvas currentWidth =' + this.currentWidth + ' currentHeight=' + this.currentHeight)
this.lastWidth = this.currentWidth
this.lastHeight = this.currentHeight
this.imageKnifeExecute()
}
}
}else{
LogUtil.log('ImageKnifeComponent onAreaChange Error newValue is undefined')
}
})
.onReady(() => {
@ -587,7 +591,7 @@ export struct ImageKnifeComponent {
aboutToDisappear() {
LogUtil.log('ImageKnifeComponent aboutToDisappear happened!')
if(this.detachFromLayout){
if(this.detachFromLayout != undefined){
this.detachFromLayout.detach();
}

View File

@ -84,4 +84,45 @@ export class ImageKnifeData {
isResource(): boolean {
return ImageKnifeType.RESOURCE == this.imageKnifeType;
}
release(){
if(this.isPixelMap()){
if(this.drawPixelMap != undefined && this.drawPixelMap.imagePixelMap != undefined){
this.drawPixelMap.imagePixelMap.release()
.then(()=>{
if(this.drawPixelMap != undefined && this.drawPixelMap.imagePixelMap !=undefined){
this.drawPixelMap.imagePixelMap = undefined;
}
})
}
}
if(this.isGIFFrame()){
if(this.drawGIFFrame != undefined){
let gifFrames = this.drawGIFFrame.imageGIFFrames;
if(gifFrames != undefined){
for (let i = 0; i < gifFrames.length; i++) {
let tempFrame = gifFrames[i];
if(tempFrame.drawPixelMap != undefined){
tempFrame.drawPixelMap.release()
}
}
this.drawGIFFrame.imageGIFFrames = undefined
}
}
}
if(this.isString()){
if(this.drawString != undefined && this.drawString.imageString!=undefined){
this.drawString.imageString = undefined
}
}
if(this.isResource()){
if(this.drawResource != undefined && this.drawResource.imageResource != undefined){
this.drawResource.imageResource = undefined
}
}
}
}

View File

@ -16,7 +16,7 @@ import {ImageKnife} from './ImageKnife'
import { DiskStrategy } from "../cache/diskstrategy/DiskStrategy"
import { AsyncCallback } from "../imageknife/interface/AsyncCallback"
import { AsyncSuccess } from "../imageknife/interface/AsyncSuccess"
import { IAllCacheInfoCallback } from "../imageknife/interface/IAllCacheInfoCallback"
import { IAllCacheInfoCallback } from "../imageknife/interface/IAllCacheInfoCallback"
import { AUTOMATIC } from "../cache/diskstrategy/enum/AUTOMATIC"
import { BaseTransform } from "../imageknife/transform/BaseTransform"
import { RotateImageTransformation } from "../imageknife/transform/RotateImageTransformation"
@ -140,7 +140,7 @@ export class RequestOption {
generateUUID(): string {
let d = new Date().getTime();
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( new RegExp("[xy]","g"), (c) => {
const r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);

View File

@ -98,10 +98,11 @@ export class Engine {
}
imageResource.createPixelMap(options)
.then(bitmap => {
imageResource.release()
})
.catch((error: BusinessError) => {
this.mCompressListener.onError("ptah createPixelMap fail,because error:" + JSON.stringify(error as BusinessError))
imageResource.release()
})
@ -173,11 +174,13 @@ export class Engine {
if (this.mCompressListener) {
this.mCompressListener.onSuccess(bitmap, path);
}
imageRes.release()
})
.catch((error:BusinessError) => {
if (this.mCompressListener) {
this.mCompressListener.onError("buffer generated pixelMap fail,because error:" + JSON.stringify(error as BusinessError))
}
imageRes.release()
})
}

View File

@ -51,7 +51,10 @@ export class RecourseProvider extends CompressAdapter {
.then(data => {
let buffer = this.uint8ArrayToBuffer(data);
let fileTypeUtil = new FileTypeUtil()
this._mPixelMapHeader = fileTypeUtil.getFileType(buffer);
let fileType = fileTypeUtil.getFileType(buffer);
if(fileType != null) {
this._mPixelMapHeader = fileType;
}
callback.compressDataListener(buffer);
})
.catch((err: BusinessError) => {

View File

@ -66,9 +66,11 @@ export namespace Crop {
} else {
func?.cropCallback("", data);
}
imageSource.release()
})
.catch((e:BusinessError) => {
func?.cropCallback(e, null);
imageSource.release()
})
}
}

View File

@ -893,6 +893,7 @@ export class Options {
if (readyCrop) {
readyCrop();
}
imageSource.release()
})
});

View File

@ -53,23 +53,28 @@ export class ErrorHolderManager<T> {
if (typeof res.id != 'undefined' && typeof res.id != 'undefined') {
let resourceFetch = new ParseResClient();
let suc = (arraybuffer:ArrayBuffer) => {
let fileTypeUtil:FileTypeUtil = new FileTypeUtil();
let typeValue:string = fileTypeUtil.getFileType(arraybuffer);
switch (typeValue) {
case SupportFormat.svg:
this.svgProcess(onComplete, onError, arraybuffer, typeValue)
break;
case SupportFormat.jpg:
case SupportFormat.png:
case SupportFormat.bmp:
case SupportFormat.gif:
case SupportFormat.tiff:
case SupportFormat.webp:
this.mediaImageProcess(onComplete, onError, arraybuffer, typeValue)
break;
default:
onError("ErrorHolderManager 文件类型不支持")
break;
let fileTypeUtil: FileTypeUtil = new FileTypeUtil();
let typeValue: string | null = fileTypeUtil.getFileType(arraybuffer);
if (typeValue != null) {
switch (typeValue) {
case SupportFormat.svg:
this.svgProcess(onComplete, onError, arraybuffer, typeValue)
break;
case SupportFormat.jpg:
case SupportFormat.png:
case SupportFormat.bmp:
case SupportFormat.gif:
case SupportFormat.tiff:
case SupportFormat.webp:
this.mediaImageProcess(onComplete, onError, arraybuffer, typeValue)
break;
default:
onError("ErrorHolderManager 文件类型不支持")
break;
}
}else{
onError("ErrorHolderManager 文件类型为null,请检查数据源arraybuffer")
}
}
resourceFetch.loadResource(res, suc, onError)

View File

@ -22,9 +22,11 @@ import { LoadDataShareFileClient } from './LoadDataShareFileClient'
import loadRequest from '@ohos.request';
import { ImageKnifeGlobal } from '../ImageKnifeGlobal'
import common from '@ohos.app.ability.common'
import { NetworkDownloadClient } from './NetworkDownloadClient'
// 数据加载器
export class DownloadClient implements IDataFetch {
private networkDownloadClient = new NetworkDownloadClient();
private httpDownloadClient = new HttpDownloadClient();
private localFileClient = new LoadLocalFileClient();
private dataShareFileClient = new LoadDataShareFileClient();
@ -42,6 +44,7 @@ export class DownloadClient implements IDataFetch {
this.dataShareFileClient.loadData(request, onCompleteFunction, onErrorFunction)
} else {
// 网络下载
// this.networkDownloadClient.loadData(request, onCompleteFunction, onErrorFunction)
this.httpDownloadClient.loadData(request, onCompleteFunction, onErrorFunction)
}
}

View File

@ -33,7 +33,7 @@ export class HttpDownloadClient implements IDataFetch {
loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) {
try {
let httpRequest = http.createHttp()
let arrayBuffers = new Array();
let arrayBuffers = new Array<ArrayBuffer>();
httpRequest.on('headersReceive', (header: Object) => {
// 跟服务器连接成功准备下载
if (request.progressFunc) {
@ -48,9 +48,11 @@ export class HttpDownloadClient implements IDataFetch {
httpRequest.on('dataReceiveProgress', (data: RequestData) => {
// 下载进度
let percent = Math.round(((data.receiveSize * 1.0) / (data.totalSize * 1.0)) * 100)
if (request.progressFunc) {
request.progressFunc.asyncSuccess(percent)
if(data != undefined && (typeof data.receiveSize == 'number') && (typeof data.totalSize == 'number') ) {
let percent = Math.round(((data.receiveSize * 1.0) / (data.totalSize * 1.0)) * 100)
if (request.progressFunc) {
request.progressFunc.asyncSuccess(percent)
}
}
})
@ -73,12 +75,7 @@ export class HttpDownloadClient implements IDataFetch {
if (!err && data == 200) {
} else {
httpRequest.off('headersReceive');
httpRequest.off('dataReceive');
httpRequest.off('dataReceiveProgress');
httpRequest.off('dataEnd');
httpRequest.destroy()
onError('HttpDownloadClient err message =' + err.message)
onError(`HttpDownloadClient has error, http code = ${data}`)
}
}
)

View File

@ -433,7 +433,12 @@ export class RequestManager {
// 步骤二: 文件名保存一份全局
// 步骤三:查看文件是否支持 非支持类型直接返回
let fileTypeUtil = new FileTypeUtil();
let filetype = fileTypeUtil.getFileType(source);
let filetype:string|null = fileTypeUtil.getFileType(source);
if(filetype == null){
onError("下载文件解析后类型为null,请检查数据源!");
return;
}
if (!fileTypeUtil.isImage(source)) {
onError("暂不支持 下载文件类型!类型=" + filetype);
return;
@ -473,11 +478,15 @@ export class RequestManager {
if (this.options.transformations[0]) {
// thumbnail 缩略图部分
if (this.options.thumbSizeMultiplier) {
this.thumbnailProcess(source, filetype, onComplete, onError);
if(filetype != null) {
this.thumbnailProcess(source, filetype, onComplete, onError);
}
} else {
this.options.transformations[0].transform(source, this.options, {asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => {
if (pixelMap) {
this.saveCacheAndDisk(pixelMap, filetype, onComplete, source);
if(filetype != null) {
this.saveCacheAndDisk(pixelMap, filetype, onComplete, source);
}
} else {
onError(error);
}
@ -495,13 +504,17 @@ export class RequestManager {
this.mParseImageUtil.parseImageThumbnail(this.options.thumbSizeMultiplier, source, thumbSuccess, thumbError);
setTimeout(() => {
let success = (value: PixelMap) => {
this.saveCacheAndDisk(value, filetype, onComplete, source);
if(filetype != null) {
this.saveCacheAndDisk(value, filetype, onComplete, source);
}
}
this.mParseImageUtil.parseImage(source, success, onError)
}, this.options.thumbDelayTime)
} else {
let success = (value: PixelMap) => {
this.saveCacheAndDisk(value, filetype, onComplete, source);
if(filetype != null) {
this.saveCacheAndDisk(value, filetype, onComplete, source);
}
}
this.mParseImageUtil.parseImage(source, success, onError)
}

View File

@ -74,11 +74,13 @@ export class BlurTransformation implements BaseTransform<PixelMap> {
} else {
fastBlur.blur(data, this._mRadius, true, func);
}
imageSource.release()
})
.catch((e:BusinessError) => {
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
imageSource.release()
func?.asyncTransform(e, null);
})
})
}})
}
}

View File

@ -76,7 +76,7 @@ export class BrightnessFilterTransformation implements BaseTransform<PixelMap> {
}
}
let data = await imageSource.createPixelMap(options);
imageSource.release()
let bufferData = new ArrayBuffer(data.getPixelBytesNumber());
await data.readPixelsToBuffer(bufferData);

View File

@ -89,7 +89,7 @@ export class ContrastFilterTransformation implements BaseTransform<PixelMap> {
}
let data = await imageSource.createPixelMap(options);
imageSource.release()
let bufferData = new ArrayBuffer(data.getPixelBytesNumber());
await data.readPixelsToBuffer(bufferData);

View File

@ -75,12 +75,14 @@ export class CropCircleTransformation implements BaseTransform<PixelMap> {
imageSource.createPixelMap(options)
.then((p:PixelMap) => {
this.transformCircle(p, func);
imageSource.release()
})
.catch((e:BusinessError) => {
LogUtil.log(Constants.PROJECT_TAG + CropCircleTransformation.TAG + " transform e:" + e);
if (func!=undefined) {
func?.asyncTransform(Constants.PROJECT_TAG + CropCircleTransformation.TAG + "e" + e, null);
}
imageSource.release()
})
}

View File

@ -93,12 +93,14 @@ export class CropCircleWithBorderTransformation implements BaseTransform<PixelMa
imageSource.createPixelMap(options)
.then((pixelMap:PixelMap) => {
this.transformPixelMap(pixelMap, outWith, outHeight, func);
imageSource.release()
})
.catch((e:BusinessError) => {
LogUtil.log(Constants.PROJECT_TAG + ";CropCircleWithBorderTransformation e:" + e);
if (func!=undefined) {
func?.asyncTransform(Constants.PROJECT_TAG + ";CropCircleWithBorderTransformation e:" + e, null);
}
imageSource.release()
})
}

View File

@ -73,11 +73,13 @@ export class CropSquareTransformation implements BaseTransform<PixelMap> {
if (func != undefined) {
func?.asyncTransform("", data);
}
imageSource.release()
})
.catch((e:BusinessError) => {
if (func != undefined) {
func?.asyncTransform(Constants.PROJECT_TAG + ";CropSquareTransformation e:" + e, null);
}
imageSource.release()
})
})
.catch((error:BusinessError) => {

View File

@ -83,9 +83,11 @@ export class CropTransformation implements BaseTransform<PixelMap> {
imageSource.createPixelMap(options)
.then((data:PixelMap) => {
func?.asyncTransform("", data);
imageSource.release()
})
.catch((e:BusinessError) => {
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
imageSource.release()
func?.asyncTransform(e, null);
})
}})

View File

@ -68,7 +68,7 @@ export class GrayscaleTransformation implements BaseTransform<PixelMap> {
}
}
let data:PixelMap= await imageSource.createPixelMap(options);
imageSource.release()
let bufferData = new ArrayBuffer(data.getPixelBytesNumber());
let bufferNewData = new ArrayBuffer(data.getPixelBytesNumber());
await data.readPixelsToBuffer(bufferData);

View File

@ -77,7 +77,7 @@ export class InvertFilterTransformation implements BaseTransform<PixelMap> {
}
let data:PixelMap = await imageSource.createPixelMap(options);
imageSource.release()
let bufferData = new ArrayBuffer(data.getPixelBytesNumber());
await data.readPixelsToBuffer(bufferData);

View File

@ -79,10 +79,12 @@ export class KuwaharaFilterTransform implements BaseTransform<PixelMap> {
imageSource.createPixelMap(options)
.then((data) => {
this.kuwahara(data, targetWidth, targetHeight, func);
imageSource.release()
})
.catch((e:BusinessError) => {
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
func?.asyncTransform(e, null);
imageSource.release()
})
}})
}

View File

@ -79,9 +79,11 @@ export class MaskTransformation implements BaseTransform<PixelMap> {
imageSource.createPixelMap(options)
.then(data => {
this.openInternal(data, targetWidth, targetHeight, func)
imageSource.release()
})
.catch((e:BusinessError )=> {
func?.asyncTransform(e, null);
imageSource.release()
})
}

View File

@ -80,9 +80,11 @@ export class PixelationFilterTransformation implements BaseTransform<PixelMap> {
} else {
pixelUtils.pixel(data, this._mPixel, func);
}
imageSource.release()
})
.catch((e:BusinessError) => {
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
imageSource.release();
func?.asyncTransform(e, null);
})
}})

View File

@ -70,9 +70,11 @@ export class RotateImageTransformation implements BaseTransform<PixelMap> {
imageSource.createPixelMap(options)
.then((data) => {
func?.asyncTransform("", data);
imageSource.release()
})
.catch((e:BusinessError) => {
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
imageSource.release()
func?.asyncTransform(e, null);
})
}})

View File

@ -113,8 +113,10 @@ export class RoundedCornersTransformation implements BaseTransform<PixelMap> {
if (func != undefined && this.mTransform_pixelMap != undefined) {
func?.asyncTransform("", this.mTransform_pixelMap);
}
imageSource.release()
})
.catch((error:BusinessError) => {
imageSource.release()
LogUtil.log(Constants.PROJECT_TAG + "RoundedCornersTransformation error:" + error);
});
})

View File

@ -73,7 +73,7 @@ export class SepiaFilterTransformation implements BaseTransform<PixelMap> {
}
}
let data:PixelMap = await imageSource.createPixelMap(options);
imageSource.release();
let bufferData = new ArrayBuffer(data.getPixelBytesNumber());
await data.readPixelsToBuffer(bufferData);

View File

@ -66,9 +66,11 @@ export class SketchFilterTransformation implements BaseTransform<PixelMap> {
} else {
CalculatePixelUtils.sketch(data, func);
}
imageSource.release()
})
.catch((e:BusinessError) => {
func?.asyncTransform(e, null);
imageSource.release()
})
}})
}

View File

@ -81,8 +81,10 @@ export class SwirlFilterTransformation implements BaseTransform<PixelMap> {
imageSource.createPixelMap(options)
.then((data) => {
this.swirl(data, this.radius, request, func);
imageSource.release();
})
.catch((e:BusinessError) => {
imageSource.release();
func?.asyncTransform(e, null);
})
}})

View File

@ -83,9 +83,11 @@ export class ToonFilterTransform implements BaseTransform<PixelMap> {
}
imageSource.createPixelMap(options)
.then((data) => {
imageSource.release()
this.toon(data, targetWidth, targetHeight, func);
})
.catch((e:BusinessError) => {
imageSource.release()
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
func?.asyncTransform(e, null);
})

View File

@ -63,7 +63,9 @@ export class TransformUtils {
editable: true,
rotate: degreesToRotate
}
return imageSource.createPixelMap(options);
let promise:Promise<PixelMap> = imageSource.createPixelMap(options);
imageSource.release()
return promise;
}
static centerInside(buf: ArrayBuffer, outWidth: number, outHeihgt: number,
@ -74,7 +76,9 @@ export class TransformUtils {
let pw = p.size.width;
let ph = p.size.height;
if (pw <= outWidth && ph <= outHeihgt) {
callback?.asyncTransform('', imageSource.createPixelMap());
let promise:Promise<PixelMap> = imageSource.createPixelMap()
imageSource.release()
callback?.asyncTransform('', promise);
} else {
TransformUtils.fitCenter(buf, outWidth, outHeihgt, callback);
}
@ -112,7 +116,9 @@ export class TransformUtils {
desiredSize: { width: targetWidth, height: targetHeight }
}
if (callback) {
callback.asyncTransform('', imageSource.createPixelMap(options));
let promise:Promise<PixelMap> = imageSource.createPixelMap(options);
imageSource.release();
callback.asyncTransform('', promise);
}
})
.catch((e:BusinessError) => {

View File

@ -88,8 +88,10 @@ export class VignetteFilterTransform implements BaseTransform<PixelMap> {
imageSource.createPixelMap(options)
.then((data) => {
this.vignette(data, targetWidth, targetHeight, func);
imageSource.release();
})
.catch((e:BusinessError) => {
imageSource.release()
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
func?.asyncTransform(e, null);
})

View File

@ -57,16 +57,26 @@ export class FileTypeUtil {
getFileType(file: ArrayBuffer): string | null {
const fileData = new Uint8Array(file);
for (const fileType in this.fileSignatureMap) {
const bufferList = this.fileSignatureMap[fileType];
for (let i = 0; i < bufferList.length; i++) {
let signature = bufferList[i];
if(this.matchesSignature(fileData,signature)){
return fileType;
let hasMatched = false;
let matchedFileType = ''
Object.keys(this.fileSignatureMap).map((fileType)=>{
if(!hasMatched) {
const bufferList = this.fileSignatureMap[fileType];
for (let i = 0; i < bufferList.length; i++) {
let signature = bufferList[i];
if (this.matchesSignature(fileData, signature)) {
hasMatched = true;
matchedFileType = fileType;
break
}
}
}else{
// 由于map函数会输出所有的keys, 所以匹配之后不再需要执行任何逻辑
}
})
if(hasMatched){
return matchedFileType;
}
return null; // 若无法识别文件类型返回null
}

View File

@ -48,6 +48,7 @@ export class ParseImageUtil implements IParseImage<PixelMap> {
} else {
onCompleteFunction(pixelmap);
}
imageSource.release()
})
})

View File

@ -131,64 +131,4 @@ export class EasyLinkedHashMap<K, V> {
this.head = null;
this.tail=null;
}
}
function testLinkedHashMap(){
// 创建一个新的 LinkedHashMap 实例
let linkedHashMap = new EasyLinkedHashMap<string, number>();
// 添加键值对,并验证插入是否正确
linkedHashMap.put('key1', 1);
linkedHashMap.put('key2', 2);
linkedHashMap.put('key3', 3);
console.log('dodo '+linkedHashMap.get('key1')); // 1
console.log('dodo '+linkedHashMap.get('key2')); // 2
console.log('dodo '+linkedHashMap.get('key3')); // 3
// 验证键值对更新是否正确
linkedHashMap.put('key2', 20);
console.log('dodo '+linkedHashMap.get('key2')); // 20
// 验证移除键值对是否正确
linkedHashMap.remove('key1');
console.log('dodo '+linkedHashMap.get('key1')); // undefined
console.log('dodo '+linkedHashMap.size()); // 2
// 添加更多键值对
linkedHashMap.put('key4', 4);
linkedHashMap.put('key5', 5);
linkedHashMap.put('key6', 6);
console.log('dodo '+linkedHashMap.size()); // 5
// 验证访问顺序是否正确,最近访问的键值对应该在链表尾部
console.log('dodo '+linkedHashMap.get('key2')); // 20
console.log('dodo '+linkedHashMap.get('key3')); // 3
console.log('dodo '+linkedHashMap.get('key4')); // 4
// 添加更多键值对,超出容量限制(假设容量限制为 5
linkedHashMap.put('key7', 7);
linkedHashMap.put('key8', 8);
linkedHashMap.put('key9', 9);
console.log('dodo '+linkedHashMap.size()); // 5
console.log('dodo '+linkedHashMap.get('key1')); // undefined因为已经超过容量限制最早访问的键值对被移除
// 验证移除最近访问的键值对后,访问顺序是否正确
linkedHashMap.remove('key4');
console.log('dodo '+linkedHashMap.get('key4')); // undefined
console.log('dodo '+linkedHashMap.get('key2')); // 20
console.log('dodo '+linkedHashMap.get('key3')); // 3
// 清空 LinkedHashMap
linkedHashMap.remove('key2');
linkedHashMap.remove('key3');
linkedHashMap.remove('key5');
linkedHashMap.remove('key6');
linkedHashMap.remove('key7');
linkedHashMap.remove('key8');
linkedHashMap.remove('key9');
console.log('dodo '+linkedHashMap.size()); // 0
}

View File

@ -14,11 +14,7 @@
*/
import { IParseGif } from './IParseGif'
import { Dims, GIFFrame } from './GIFFrame'
import { LoadType } from '../../../../../../../GifWorker'
import { parseBufferToFrame } from './parse/GIFParse'
import { LogUtil } from '../../utils/LogUtil'
import image from '@ohos.multimedia.image'
import { ImageKnifeGlobal } from '../../ImageKnifeGlobal'
import { BusinessError } from '@ohos.base'
import worker, { ErrorEvent, MessageEvents } from '@ohos.worker';
@ -35,143 +31,49 @@ export interface gifBackData{
transparentIndex:number[]
}
export class GIFParseImpl implements IParseGif {
//
parseGifs(imageinfo: ArrayBuffer, callback: (data?:GIFFrame[], err?:BusinessError|string) => void, worker?:worker.ThreadWorker,runMainThread?:boolean) {
let resolveWorker = worker;
LogUtil.log('parseGifs resolveWorker1 is null =' + (resolveWorker == null))
if (!resolveWorker && ImageKnifeGlobal.getInstance().getImageKnife() != undefined) {
resolveWorker = ImageKnifeGlobal.getInstance().getImageKnife()?.getGifWorker();
// 硬解码流程
let imageSource = image.createImageSource(imageinfo);
let decodeOpts: image.DecodingOptions = {
sampleSize: 1,
editable: true,
rotate: 0
}
LogUtil.log('parseGifs resolveWorker2 is null =' + (resolveWorker == null))
if (!!resolveWorker && !runMainThread) {
LogUtil.log('parseGifs in worker thread!')
let copyBuffer = imageinfo.slice(0);
this.useWorkerParse(resolveWorker, copyBuffer, (data, err) => {
if (err) {
callback(undefined, err)
} else {
if (data != undefined) {
this.createPixelMapAll(data).then((pixelmaps) => {
if (pixelmaps.length == data.length) {
for (let i = 0;i < data.length; i++) {
let frame = data[i];
frame['drawPixelMap'] = pixelmaps[i];
frame['patch'] = undefined;
}
callback(data, undefined)
let data:GIFFrame[] = [];
imageSource.createPixelMapList(decodeOpts).then((pixelList: Array<PixelMap>) => {
//sdk的api接口发生变更从.getDelayTime() 变为.getDelayTimeList()
imageSource.getDelayTimeList().then(delayTimes => {
if (pixelList.length > 0) {
let pixelmap1 = pixelList[0];
pixelmap1.getImageInfo().then(imageInfo => {
for (let i = 0; i < pixelList.length; i++) {
let frame = new GIFFrame();
frame.drawPixelMap = pixelList[i];
frame.dims = { width: imageInfo.size.width, height: imageInfo.size.height, top: 0, left: 0 }
if (i < delayTimes.length) {
frame.delay = delayTimes[i];
} else {
frame.delay = delayTimes[delayTimes.length - 1]
}
}).catch((err: BusinessError) => {
callback(undefined, err)
})
}else{
callback(undefined, 'GIF Parse Error callback data is undefined')
}
data.push(frame)
}
callback(data,undefined)
imageSource.release();
}).catch((err: string) => {
imageSource.release();
callback(undefined,err)
})
}
}).catch((err: string) => {
imageSource.release();
callback(undefined,err)
})
} else {
LogUtil.log('parseGifs in main thread!')
let frames = parseBufferToFrame(imageinfo)
LogUtil.log('frames length =' + frames.length)
this.createPixelMapAll(frames).then((pixelmaps) => {
if (pixelmaps.length == frames.length) {
for (let i = 0;i < frames.length; i++) {
let frame = frames[i];
frame['drawPixelMap'] = pixelmaps[i];
frame['patch'] = undefined;
}
LogUtil.log('parseGifs in main thread! callback is done!')
callback(frames, undefined)
}
}).catch((err:BusinessError) => {
LogUtil.log('parseGifs in main thread! err =' + err)
callback(undefined, err)
})
}
}
private createPixelMapAll(frames:GIFFrame[]): Promise<PixelMap[]> {
let promises:Promise<PixelMap>[] = new Array();
let filterCriteria = (item:GIFFrame) => {
if (!item['drawPixelMap']) {
return true;
}
return false;
}
frames.filter(filterCriteria, frames).flatMap<void,undefined>((frame:GIFFrame) => {
if(frame.patch != undefined) {
promises.push(image.createPixelMap(frame.patch.buffer, {
'size': {
'height': frame.dims.height as number,
'width': frame.dims.width as number
}
}))
}
}).catch((err: string) => {
imageSource.release();
callback(undefined,err)
})
return Promise.all<PixelMap>(promises)
}
private useWorkerParse(worker:worker.ThreadWorker, buffer: ArrayBuffer, callback: (data?:GIFFrame[], err?:BusinessError|string) => void) {
worker.onerror = (err:ErrorEvent)=>{
callback(undefined, err.message)
}
worker.onmessageerror = (event: MessageEvents) => {
callback(undefined, event.type)
}
worker.onexit = ()=> {
LogUtil.log('gifWork worker.onexit!')
}
worker.onmessage = (e: MessageEvents) => {
let data:Record<string,Object> = e.data;
switch (data.type as string) {
case LoadType.loadBufferByWorker:
let pages:gifBackData = (data.data as gifBackData);
if (this.gifDecodeCorrect(pages)) {
let images = this.recDecodedData(pages);
callback(images, undefined)
} else {
callback(undefined, 'GIF Worker Decoder Data Is Error!')
}
break;
default:
break
}
}
let obj:senderData = { type: LoadType.loadBufferByWorker, data: buffer }
worker.postMessage(obj, [buffer])
}
private gifDecodeCorrect(frames:gifBackData) {
if (
(frames.patch.length == frames.dims.length) &&
(frames.patch.length == frames.delay.length) &&
(frames.patch.length == frames.disposalType.length) &&
(frames.patch.length == frames.patch.length) &&
(frames.patch.length == frames.transparentIndex.length)
) {
return true;
}
return false;
}
// 子线程数据回传处理
private recDecodedData(pages:gifBackData): GIFFrame[] {
let images:GIFFrame[] = new Array()
for (let i = 0; i < pages.patch.length; i++) {
let frame = new GIFFrame();
frame['dims'] = pages.dims[i]
frame['delay'] = pages.delay[i]
frame['disposalType'] = pages.disposalType[i]
let uint8ClampedArray = new Uint8ClampedArray(pages.patch[i])
frame['patch'] = uint8ClampedArray
frame['transparentIndex'] = pages.transparentIndex[i]
images.push(frame)
}
return images;
}
}

View File

@ -6,6 +6,6 @@
"name": "imageknife",
"description": "example description",
"repository": {},
"version": "2.1.1-rc.0",
"version": "2.1.1-rc.3",
"dependencies": {}
}