diff --git a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets index 3fec8c9..1cc2ca2 100644 --- a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets +++ b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets @@ -290,6 +290,10 @@ struct IndexFunctionDemo { .onClick(() => { router.pushUrl({ url: "pages/testManyNetImageLoadWithPage" }); }).margin({ top: 5, left: 3 }) + Button("多线程加载大量网络图片加载速度") + .onClick(() => { + router.pushUrl({ url: "pages/testManyNetImageLoadWithPage2" }); + }).margin({ top: 5, left: 3 }) Button("测试多张gif加载位置") .onClick(() => { router.pushUrl({ url: "pages/testManyGifLoadWithPage" }); diff --git a/entry/src/main/ets/pages/photosPausedResumedPage.ets b/entry/src/main/ets/pages/photosPausedResumedPage.ets index 4105d79..d82f1fd 100644 --- a/entry/src/main/ets/pages/photosPausedResumedPage.ets +++ b/entry/src/main/ets/pages/photosPausedResumedPage.ets @@ -18,9 +18,8 @@ import {ImageKnifeGlobal} from '@ohos/libraryimageknife' import {RotateImageTransformation} from '@ohos/libraryimageknife' import {Material} from './model/Material' import {TestDataSource} from './model/TestDataSource' -import {DiskLruCache} from '@ohos/disklrucache' -import ArkWorker from '@ohos.worker' -import Prompt from '@system.prompt' + + @Entry @Component struct photosPausedResumedPage { diff --git a/entry/src/main/ets/pages/testManyNetImageLoadWithPage2.ets b/entry/src/main/ets/pages/testManyNetImageLoadWithPage2.ets new file mode 100644 index 0000000..f1acd01 --- /dev/null +++ b/entry/src/main/ets/pages/testManyNetImageLoadWithPage2.ets @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { ScaleType, ImageKnifeComponent, NONE, DiskStrategy } from "@ohos/libraryimageknife" +import { Material } from './model/Material'; +import { TestDataSource } from './model/TestDataSource'; + + +@Entry +@Component +struct TestManyNetImageLoadWithPage2 { + private data: TestDataSource = new TestDataSource(); + private setting: DiskStrategy = new NONE(); + + build() { + Scroll() { + Column() { + Grid() { + LazyForEach(this.data, (item: Material, index) => { + GridItem() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: item.thumbnail, + placeholderSrc: $r('app.media.icon_loading'), + mainScaleType: ScaleType.CENTER_CROP, + placeholderScaleType: ScaleType.CENTER_CROP, + isCacheable: false, + // strategy: this.setting + } + }).width('100%').height('100%') + }.width('45%').height(200) + }, (item: string) => JSON.stringify(item)) + } + .columnsTemplate('1fr 1fr') + .columnsGap(8) + .rowsGap(10) + .width('100%') + .hitTestBehavior(HitTestMode.None) + .maxCount(10) + .cachedCount(5) + }.margin({ top: 5 }) + } + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index e06a871..3670de9 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -34,6 +34,7 @@ "pages/hspCacheTestPage", "pages/multiHspTestPage", "pages/testManyNetImageLoadWithPage", + "pages/testManyNetImageLoadWithPage2", "pages/testManyGifLoadWithPage", "pages/testImageAntiAliasingWithPage", "pages/testImageKnifeRouter1", diff --git a/entry/src/ohosTest/ets/test/List.test.ets b/entry/src/ohosTest/ets/test/List.test.ets index d42ebce..b04b330 100644 --- a/entry/src/ohosTest/ets/test/List.test.ets +++ b/entry/src/ohosTest/ets/test/List.test.ets @@ -18,10 +18,12 @@ import LogUtilTest from './logutil.test' import Transform from './transfrom.test' import RequestOptionTest from './requestoption.test' import ImageKnifeTest from './imageknife.test' +import DiskLruCacheTest from './diskLruCache.test' export default function testsuite() { abilityTest() lruCacheTest() + DiskLruCacheTest() LogUtilTest() Transform() RequestOptionTest() diff --git a/entry/src/ohosTest/ets/test/diskLruCache.test.ets b/entry/src/ohosTest/ets/test/diskLruCache.test.ets new file mode 100644 index 0000000..80840c2 --- /dev/null +++ b/entry/src/ohosTest/ets/test/diskLruCache.test.ets @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import hilog from '@ohos.hilog'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect, Hypium } from '@ohos/hypium' +import { DiskLruCache, ImageKnifeGlobal, ImageKnife, DiskCacheEntry } from '@ohos/imageknife' +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import { AbilityConstant, common, UIAbility, Want } from '@kit.AbilityKit'; +import fs from '@ohos.file.fs'; + +const BASE_COUNT: number = 2000; + +export default function DiskLruCacheTest() { + + describe('DiskLruCacheTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('testSetCahe', 0, () => { + let context: Object | undefined = ImageKnifeGlobal.getInstance().getHapContext(); + if (context != undefined) { + let disLruCache: DiskLruCache = DiskLruCache.create(context as common.UIAbilityContext, 1024); + let startTime = new Date().getTime(); + disLruCache.set('test', "Hello World Simple Example."); + disLruCache.set('testABC', "Hello World ABC"); + disLruCache.set('testDE', "Hello World Simple DE"); + expect(String.fromCharCode(...new Uint8Array(disLruCache.get('test') as ArrayBufferLike)) == "Hello World Simple Example.") + .assertTrue() + expect(String.fromCharCode(...new Uint8Array(disLruCache.get('testABC') as ArrayBufferLike)) == "Hello World ABC") + .assertTrue() + expect(String.fromCharCode(...new Uint8Array(disLruCache.get('testDE') as ArrayBufferLike)) == "Hello World Simple DE") + .assertTrue() + let str: string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + for (let index = 0; index < 100; index++) { + str += index; + disLruCache.set('test' + index, str); + } + expect(disLruCache.getSize() <= 1024).assertTrue() + endTime(startTime, 'testSetCahe'); + disLruCache.cleanCacheData(); + } + + }) + it('testGetCacheAsync', 1, () => { + + let context: common.UIAbilityContext = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext; + if (context != undefined) { + let data = context.filesDir + let cache: DiskLruCache = DiskLruCache.create(context); + let startTime = new Date().getTime(); + let path = data + '/testFile.txt'; + let file = fs.openSync(path, 0o102); + fs.writeSync(file.fd, "hello, world!"); + let length = fs.statSync(path).size; + let dataArr = new ArrayBuffer(length); + fs.readSync(file.fd, dataArr); + cache.set('testFile', dataArr); + expect(cache.get('testFile')?.byteLength == 13).assertTrue() + endTime(startTime, 'testGetCacheAsync'); + cache.cleanCacheData(); + } + }) + it('testDeleteCacheDataByKey', 2, () => { + let context: common.UIAbilityContext = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext; + if (context != undefined) { + let cache: DiskLruCache = DiskLruCache.create(context); + let startTime = new Date().getTime(); + cache.set('test', "Hello World Simple Example."); + cache.set('testABC', "Hello World ABC"); + expect(String.fromCharCode(...new Uint8Array(cache.get('test') as ArrayBufferLike))) + .assertEqual("Hello World Simple Example."); + expect(String.fromCharCode(...new Uint8Array(cache.get('testABC') as ArrayBufferLike))) + .assertEqual("Hello World ABC"); + cache.deleteCacheDataByKey('test'); + cache.deleteCacheDataByKey('testABC'); + expect(String.fromCharCode(...new Uint8Array(cache.get('test') as ArrayBufferLike))).assertEqual(''); + expect(String.fromCharCode(...new Uint8Array(cache.get('testABC') as ArrayBufferLike))).assertEqual(''); + endTime(startTime, 'testDeleteCacheDataByKey'); + cache.cleanCacheData(); + } + }) + it('testGetPath', 3, async () => { + let context: common.UIAbilityContext = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext; + if (context != undefined) { + let cache: DiskLruCache = DiskLruCache.create(context); + let startTime = new Date().getTime(); + cache.set('test', "Hello World Simple Example."); + let path: string = '' + await cache.getFileToPathAsync('test').then((data) => { + path = data + }) + expect(String.fromCharCode(...new Uint8Array(cache.get('test') as ArrayBufferLike)) == "Hello World Simple Example.") + .assertTrue() + expect(cache.getFileToPath('test') == path).assertTrue() + expect(cache.getPath() + cache.getCacheMap().getFirstKey() == path).assertTrue() + endTime(startTime, 'testGetPath'); + cache.cleanCacheData(); + } + + }) + it('testGetCacheMap', 6, async () => { + let context: common.UIAbilityContext = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext; + if (context != undefined) { + let cache: DiskLruCache = DiskLruCache.create(context); + let startTime = new Date().getTime(); + cache.set('test', "Hello World Simple Example."); + expect(cache.getCacheMap().getFirstKey() == '098f6bcd4621d373cade4e832627b4f6').assertTrue() + expect(cache.getCacheMap().hasKey('098f6bcd4621d373cade4e832627b4f6') == true).assertTrue() + endTime(startTime, 'testGetCacheMap'); + cache.cleanCacheData(); + } + }) + + it('testGetSize', 7, async () => { + let context: common.UIAbilityContext = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext; + if (context != undefined) { + let cache: DiskLruCache = DiskLruCache.create(context); + let startTime = new Date().getTime(); + cache.set('test', "Hello World Simple Example."); + expect(String.fromCharCode(...new Uint8Array(cache.get('test') as ArrayBufferLike)) == "Hello World Simple Example.") + .assertTrue() + expect(cache.getSize() == 27).assertTrue() + endTime(startTime, 'testGetSize'); + cache.cleanCacheData(); + } + }) + + it('testDiskCacheEntry', 8, async () => { + let startTime = new Date().getTime(); + let dentry = new DiskCacheEntry('test', 30 * 1024 * 1024) + expect(dentry.getKey() == 'test').assertTrue() + dentry.setKey('newtest') + expect(dentry.getKey() == 'newtest').assertTrue() + expect(dentry.getLength() == 30 * 1024 * 1024).assertTrue() + dentry.setLength(12 * 1024 * 1024) + expect(dentry.getLength() == 12 * 1024 * 1024).assertTrue() + expect(dentry.toString() == dentry.getKey() + ' - ' + dentry.getLength()).assertTrue() + endTime(startTime, 'testDiskCacheEntry'); + }) + }) +} + +function endTime(startTime: number, tag: string) { + let endTime: number = new Date().getTime(); + let averageTime = ((endTime - startTime) * 1000 / BASE_COUNT) + console.info(tag + " startTime: " + endTime) + console.info(tag + " endTime: " + endTime) + console.log(tag + " averageTime: " + averageTime + "μs"); +} \ No newline at end of file diff --git a/library/index.ets b/library/index.ets index 09171b9..4edcd48 100644 --- a/library/index.ets +++ b/library/index.ets @@ -21,6 +21,8 @@ export { FileUtils } from './src/main/ets/components/cache/FileUtils' export { Base64 } from './src/main/ets/components/cache/Base64' export { LruCache } from './src/main/ets/components/cache/LruCache' +export { DiskLruCache } from './src/main/ets/components/cache/DiskLruCache' +export { DiskCacheEntry } from './src/main/ets/components/cache/DiskCacheEntry' export { DiskStrategy } from './src/main/ets/components/cache/diskstrategy/DiskStrategy' export { ALL } from './src/main/ets/components/cache/diskstrategy/enum/ALL' export { AUTOMATIC } from './src/main/ets/components/cache/diskstrategy/enum/AUTOMATIC' diff --git a/library/oh-package.json5 b/library/oh-package.json5 index 308fb82..1a545b0 100644 --- a/library/oh-package.json5 +++ b/library/oh-package.json5 @@ -17,7 +17,6 @@ "version": "2.1.2-rc.8", "dependencies": { "pako": "^2.1.0", - "@ohos/disklrucache": "^2.0.2-rc.0", "@ohos/gpu_transform": "^1.0.0" }, "tags": [ diff --git a/library/src/main/ets/components/cache/DiskCacheEntry.ets b/library/src/main/ets/components/cache/DiskCacheEntry.ets new file mode 100644 index 0000000..26edc97 --- /dev/null +++ b/library/src/main/ets/components/cache/DiskCacheEntry.ets @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export class DiskCacheEntry { + // 缓存的key + key: string = '' + + // 缓存文件大小 + length: number = 0 + + constructor(key: string, length?: number) { + this.key = key + this.length = length as number + } + + setKey(key: string) { + this.key = key + } + + getKey(): string { + return this.key + } + + setLength(length: number) { + this.length = length + } + + getLength(): number { + return this.length + } + + toString(): string { + return this.key + ' - ' + this.length + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/cache/DiskLruCache.ets b/library/src/main/ets/components/cache/DiskLruCache.ets new file mode 100644 index 0000000..82ad995 --- /dev/null +++ b/library/src/main/ets/components/cache/DiskLruCache.ets @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import fileio from '@ohos.fileio' +import { CustomMap } from './CustomMap' +import { FileUtils } from './FileUtils' +import { FileReader } from './FileReader' +import { DiskCacheEntry } from './DiskCacheEntry' +import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5' +import { Context } from '@ohos.abilityAccessCtrl' +import { common } from '@kit.AbilityKit' + +export class DiskLruCache { + // 默认缓存数据最大值 + private static readonly DEFAULT_MAX_SIZE: number = 300 * 1024 * 1024 + // 默认缓存文件名 + private static readonly DEFAULT_NAME: string = 'diskLruCache' + // 缓存journal文件名称 + private static readonly journal: string = 'journal' + // 缓存journal备份文件名称 + private static readonly journalTemp: string = 'journal_temp' + // 备份文件save标识符 + private static readonly SAVE: string = 'save' + // 备份文件read标识符 + private static readonly READ: string = 'read' + // 备份文件remove标识符 + private static readonly REMOVE: string = 'remove' + // 缓存文件路径地址 + private path: string = '' + // 缓存journal文件路径 + private journalPath: string = '' + // 缓存journal备份文件路径 + private journalPathTemp: string = '' + // 缓存数据最大值 + private maxSize: number = DiskLruCache.DEFAULT_MAX_SIZE + // 当前缓存数据值 + private size: number = 0 + // 缓存数据集合 + private cacheMap: CustomMap = new CustomMap() + + constructor(path: string, maxSize: number) { + this.path = path + this.maxSize = maxSize + this.journalPath = path + DiskLruCache.journal + this.journalPathTemp = path + DiskLruCache.journalTemp + } + + /** + * 打开context获取的cache路径中的缓存,如果不存在缓存,则创建新缓存 + * + * @param context 上下文 + * @param maxSize 缓存数据最大值,默认值为300M + */ + public static create(context: common.UIAbilityContext, maxSize?: number): DiskLruCache { + if (!!!context) { + throw new Error('DiskLruCache create context is empty, checking the parameter'); + } + if (!!!maxSize) { + maxSize = DiskLruCache.DEFAULT_MAX_SIZE + } + if (maxSize <= 0) { + throw new Error("DiskLruCache create maxSize <= 0, checking the parameter"); + } + + // 使用默认应用在内部存储上的缓存路径,作为存储地址 + let path = context.cacheDir + FileUtils.SEPARATOR + DiskLruCache.DEFAULT_NAME + if (!FileUtils.getInstance().existFolder(path)) { + FileUtils.getInstance().createFolder(path) + } + if (path.endsWith(FileUtils.SEPARATOR)) { + path = path + } else { + path = path + FileUtils.SEPARATOR + } + let journalPath = path + DiskLruCache.journal + let journalPathTemp = path + DiskLruCache.journalTemp + + // 判断日志文件是否存在,如果没有初始化创建 + if (FileUtils.getInstance().exist(journalPath)) { + let stat = fileio.statSync(journalPath) + if (stat.size > 0) { + FileUtils.getInstance().createFile(journalPathTemp) + FileUtils.getInstance().copyFile(journalPath, journalPathTemp) + let diskLruCache: DiskLruCache = new DiskLruCache(path, maxSize) + diskLruCache.readJournal(journalPathTemp) + diskLruCache.resetJournalFile() + return diskLruCache + } else { + return new DiskLruCache(path, maxSize) + } + } else { + FileUtils.getInstance().createFile(journalPath) + return new DiskLruCache(path, maxSize) + } + } + + /** + * 设置disk缓存最大数据值 + * + * @param max 缓存数据最大值 + */ + setMaxSize(max: number) { + if (max <= 0 || max > DiskLruCache.DEFAULT_MAX_SIZE) { + throw new Error('setMaxSize error, checking the parameter'); + } + this.maxSize = max + this.trimToSize() + } + + /** + * 存储disk缓存数据 + * + * @param key 键值 + * @param content 文件内容 + */ + set(key: string, content: ArrayBuffer | string) { + if (!!!key) { + throw new Error('key is null, checking the parameter') + } + let fileSize = 0; + if (content instanceof ArrayBuffer) { + if (content == null || content.byteLength == 0) { + throw new Error('content is null. checking the parameter') + } + fileSize = content.byteLength + } else { + if (!!!content) { + throw new Error('content is null, checking the parameter') + } + fileSize = content.length; + } + if (this.fileSizeMoreThenMaxSize(fileSize)) { + throw new Error('content must be less then DiskLruCache Size, checking the parameter') + return + } + key = SparkMD5.hashBinary(key) + this.size = this.size + fileSize + this.putCacheMap(key, fileSize) + FileUtils.getInstance().writeData(this.journalPath, DiskLruCache.SAVE + ' ' + key + FileReader.LF) + this.trimToSize() + let tempPath = this.path + key + FileUtils.getInstance().writeNewFile(tempPath, content) + } + + /** + * 异步存储disk缓存数据 + * + * @param key 键值 + * @param content 文件内容 + */ + async setAsync(key: string, content: ArrayBuffer | string): Promise { + if (!!!key) { + throw new Error('key is null, checking the parameter') + } + let fileSize = 0; + if (content instanceof ArrayBuffer) { + if (content == null || content.byteLength == 0) { + throw new Error('content is null. checking the parameter') + } + fileSize = content.byteLength + } else { + if (!!!content) { + throw new Error('content is null, checking the parameter') + } + fileSize = content.length; + } + if (this.fileSizeMoreThenMaxSize(fileSize)) { + throw new Error('content must be less then DiskLruCache Size, checking the parameter') + return + } + key = SparkMD5.hashBinary(key) + this.size = this.size + fileSize + this.putCacheMap(key, fileSize) + await FileUtils.getInstance().writeDataAsync(this.journalPath, DiskLruCache.SAVE + ' ' + key + FileReader.LF) + this.trimToSize() + let tempPath = this.path + key + await FileUtils.getInstance().writeNewFileAsync(tempPath, content) + } + + /** + * 获取key缓存数据 + * + * @param key key 键值 + */ + get(key: string): ArrayBuffer | undefined { + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key) + let path = this.path + key; + if (FileUtils.getInstance().exist(path)) { + let ab: ArrayBuffer = FileUtils.getInstance().readFile(path) + this.putCacheMap(key, ab.byteLength) + FileUtils.getInstance().writeData(this.journalPath, DiskLruCache.READ + ' ' + key + FileReader.LF) + return ab + } else { + return undefined; + } + } + + /** + * 异步获取key缓存数据 + * + * @param key 键值 + */ + async getAsync(key: string): Promise { + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key) + let path = this.path + key; + if (FileUtils.getInstance().exist(path)) { + let ab: ArrayBuffer = await FileUtils.getInstance().readFileAsync(path) + this.putCacheMap(key, ab.byteLength) + await FileUtils.getInstance().writeDataAsync(this.journalPath, DiskLruCache.READ + ' ' + key + FileReader.LF) + return ab + } else { + return undefined; + } + } + + /** + * 获取key缓存数据绝对路径 + * + * @param key 键值 + */ + getFileToPath(key: string): string { + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key); + let path = this.path + key; + if (FileUtils.getInstance().exist(path)) { + FileUtils.getInstance().writeData(this.journalPath, DiskLruCache.READ + ' ' + key + FileReader.LF); + return path + } else { + return ""; + } + } + + /** + * 异步获取key缓存数据绝对路径 + * + * @param key 键值 + */ + async getFileToPathAsync(key: string): Promise { + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key); + let path = this.path + key; + if (FileUtils.getInstance().exist(path)) { + await FileUtils.getInstance().writeDataAsync(this.journalPath, DiskLruCache.READ + ' ' + key + FileReader.LF); + return path + } else { + return "" + } + } + + /** + * 获取缓存路径 + */ + getPath(): string { + return this.path; + } + + /** + * 删除key缓存数据 + * + * @param key 键值 + */ + deleteCacheDataByKey(key: string): DiskCacheEntry { + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key) + let path = this.path + key; + if (FileUtils.getInstance().exist(path)) { + let ab = FileUtils.getInstance().readFile(path) + this.size = this.size - ab.byteLength + this.cacheMap.remove(key) + FileUtils.getInstance().writeData(this.journalPath, DiskLruCache.REMOVE + ' ' + key + FileReader.LF) + FileUtils.getInstance().deleteFile(path) + } + return this.cacheMap.get(key) as DiskCacheEntry; + } + + /** + *遍历当前的磁盘缓存数据 + * + * @param fn 遍历后方法回调 + */ + foreachDiskLruCache(fn: Function) { + this.cacheMap.each(fn()) + } + + /** + * 清除所有disk缓存数据 + */ + cleanCacheData() { + this.cacheMap.each((value, key) => { + FileUtils.getInstance().deleteFile(this.path + key) + }) + FileUtils.getInstance().deleteFile(this.journalPath) + this.cacheMap.clear() + this.size = 0 + } + + getCacheMap() { + return this.cacheMap; + } + + /** + * 返回当前DiskLruCache的size大小 + */ + getSize() { + return this.size; + } + + /** + * 处理journal文件数据 + * + * @param line 日志行字符串 + */ + private dealWithJournal(line: string) { + let filePath = '' + try { + let lineData = line.split(' ') + if (lineData.length > 1) { + if (lineData[0] != DiskLruCache.REMOVE) { + filePath = this.path + lineData[1] + let fileStat = fileio.statSync(filePath) + if (fileStat.isFile() && fileStat.size > 0) { + this.size = this.size + fileStat.size + FileUtils.getInstance().writeData(this.journalPath, line + FileReader.LF) + this.putCacheMap(lineData[1], fileStat.size) + } + } else { + if (this.cacheMap.hasKey(lineData[1])) { + let cacheEntry: DiskCacheEntry = this.cacheMap.get(lineData[1]) as DiskCacheEntry; + this.size = this.size - cacheEntry.getLength() + this.cacheMap.remove(lineData[1]) + } + } + } + } catch (e) { + console.error('DiskLruCache - dealWithJournal e ' + e) + } + } + + /** + * 重置journal文件数据 + */ + private resetJournalFile() { + FileUtils.getInstance().clearFile(this.journalPath) + for (let key of this.cacheMap.keys()) { + FileUtils.getInstance().writeData(this.journalPath, DiskLruCache.SAVE + ' ' + key + FileReader.LF) + } + } + + /** + * 读取journal文件的缓存数据 + * + * @param path 日志缓存文件路径地址 + */ + private readJournal(path: string) { + let fileReader = new FileReader(path) + let line: string = '' + while (!fileReader.isEnd()) { + line = fileReader.readLine() + line = line.replace(FileReader.LF, '').replace(FileReader.CR, '') + this.dealWithJournal(line) + } + fileReader.close() + FileUtils.getInstance().deleteFile(this.journalPathTemp) + this.trimToSize() + } + + /** + * 缓存数据map集合 + * + * @param key 键值 + * @param length 缓存文件大小 + */ + private putCacheMap(key: string, length?: number) { + if (length && length > 0) { + this.cacheMap.put(key, new DiskCacheEntry(key, length)) + } else { + this.cacheMap.put(key, new DiskCacheEntry(key)) + } + } + + /** + * 根据LRU算法删除多余缓存数据 + */ + private trimToSize() { + while (this.size > this.maxSize) { + let tempKey: string = this.cacheMap.getFirstKey() + let fileSize = FileUtils.getInstance().getFileSize(this.path + tempKey) + if (fileSize > 0) { + this.size = this.size - fileSize + } + FileUtils.getInstance().deleteFile(this.path + tempKey) + this.cacheMap.remove(tempKey) + FileUtils.getInstance().writeData(this.journalPath, DiskLruCache.REMOVE + ' ' + tempKey + FileReader.LF) + } + } + + /** + * 图片文件过大 直接超过DiskLruCache上限 + */ + private fileSizeMoreThenMaxSize(fileSize: number): boolean { + if (fileSize > this.maxSize) { + return true; + } + return false; + } + + /** + * 子线程里只写入缓存文件 + * @param context + * @param key + * @param value + */ + static saveFileCacheOnlyFile(path: string, key: string, value: ArrayBuffer): boolean { + // 写文件 + if (!!!key) { + throw new Error('key is null, checking the parameter') + } + key = SparkMD5.hashBinary(key); + FileUtils.getInstance() + .writeData(path + DiskLruCache.journal, DiskLruCache.SAVE + ' ' + key + FileReader.LF); + FileUtils.getInstance().writeNewFile(path + key, value); + return true + } + + /** + * 子线程中,通过文件名,直接查找是否有文件缓存 + * @param context + * @param key + * @returns + */ + static getFileCacheByFile(path: string, key: string): ArrayBuffer | undefined { + // 从文件获取查看是否有缓存 + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key) + let filepath = path + key; + if (FileUtils.getInstance().exist(filepath)) { + let ab: ArrayBuffer = FileUtils.getInstance().readFile(filepath) + FileUtils.getInstance().writeData(path + DiskLruCache.journal, DiskLruCache.READ + ' ' + key + FileReader.LF) + return ab + } else { + return undefined; + } + } + + // 添加缓存键值对,但不写文件(用于子线程已经写文件的场景) + setCacheMapAndSize(key: string, content: ArrayBuffer | string): void { + if (!!!key) { + throw new Error('key is null, checking the parameter') + } + let fileSize = 0; + if (content instanceof ArrayBuffer) { + if (content == null || content.byteLength == 0) { + throw new Error('content is null. checking the parameter') + } + fileSize = content.byteLength + } else { + if (!!!content) { + throw new Error('content is null, checking the parameter') + } + fileSize = content.length; + } + if (this.fileSizeMoreThenMaxSize(fileSize)) { + throw new Error('content must be less then DiskLruCache Size, checking the parameter') + return + } + key = SparkMD5.hashBinary(key); + this.size = this.size + fileSize; + this.putCacheMap(key, fileSize); + this.trimToSize(); + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/cache/FileReader.ets b/library/src/main/ets/components/cache/FileReader.ets new file mode 100644 index 0000000..a8c3269 --- /dev/null +++ b/library/src/main/ets/components/cache/FileReader.ets @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import fs, { ReadOptions } from '@ohos.file.fs'; +import { RequestOption } from '../imageknife/RequestOption'; + +export class FileReader { + // 换行符 + static readonly LF: string = '\n' + // CR符 + static readonly CR: string = '\r' + // 文件大小 + fileLength: number = 0 + // 读取的长度 + length: number = 0 + // 读写stream + stream: fs.Stream | null = null + // 缓存buf + buf: ArrayBuffer = new ArrayBuffer(1) + + /** + * 读取文件行 + * + * @param path 文件路径 + */ + constructor(path: string) { + if (!!!path) { + throw new Error('FileReader constructor path is null, checking the parameter') + return; + } + try { + this.stream = fs.createStreamSync(path, 'r+'); + let stat = fs.statSync(path) + this.fileLength = stat.size + } catch (e) { + console.log(path); + console.log(e.toString()); + } + } + + /** + * 循环读取文件数据 + */ + readLine(): string { + let line = '' + while (this.length < this.fileLength) { + this.stream?.readSync(this.buf, { length: this.length }) + this.length++ + let temp = String.fromCharCode(...new Uint8Array(this.buf)); + line = line + temp + // check CRLF + if (temp == FileReader.CR) { + // 边界判断 首先拿到下一个字符判断是否是LF 如果是CRLF需要再向后挪一位 + if (this.length < this.fileLength) { + let nextBuf = new ArrayBuffer(1) + this.stream?.readSync(nextBuf, { length: this.length }) + let nextTemp = String.fromCharCode(...new Uint8Array(nextBuf)); + // 如果是CRLF 需要给当前length+1 向后挪一位 + if (nextTemp == FileReader.LF) { + this.length++ + } + } + // 如果不是CRLF 只有一个CR结尾length不用变 + return line; + } else { + // 判断LF情况 + if (temp == FileReader.LF) { + return line + } + } + } + return line + } + + /** + * 判断文件是否结束 + */ + isEnd() { + return this.fileLength <= 0 || this.length == this.fileLength + } + + /** + * 关闭stream + */ + close() { + this.stream?.closeSync() + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/cache/FileUtils.ets b/library/src/main/ets/components/cache/FileUtils.ets index 8f2ae0e..a802bfe 100644 --- a/library/src/main/ets/components/cache/FileUtils.ets +++ b/library/src/main/ets/components/cache/FileUtils.ets @@ -16,6 +16,7 @@ import fs from '@ohos.file.fs'; import { BusinessError } from '@ohos.base' export class FileUtils { + static readonly SEPARATOR: string = '/' base64Str: string = '' private static sInstance: FileUtils; @@ -32,13 +33,21 @@ export class FileUtils { /** * 新建文件 + * * @param path 文件绝对路径及文件名 * @return number 文件句柄id */ createFile(path: string): number { - return fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).fd + let num = -1; + try { + num = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).fd + } catch (e) { + console.log("createFile err :" + e) + } + return num; } + /** * 删除文件 * @param path 文件绝对路径及文件名 @@ -143,16 +152,20 @@ export class FileUtils { /** * 判断path文件是否存在 + * + * @param path 文件绝对路径 */ exist(path: string): boolean { try { - let stat = fs.statSync(path) - return stat.isFile() - } catch (e) { - console.debug("FileUtils - fileutils exsit e" + e) - console.log("path=>" + path) - return false + if (fs.accessSync(path)) { + let stat = fs.statSync(path) + return stat.isFile() + } + } catch (error) { + let err: BusinessError = error as BusinessError; + console.error("accessSync failed with error message: " + err.message + ", error code: " + err.code); } + return false } /** @@ -318,9 +331,84 @@ export class FileUtils { return tmpUint8Array } + /** + * 异步向path写入数据 + * + * @param path 文件绝对路径 + * @param content 文件内容 + */ + async writeDataAsync(path: string, content: ArrayBuffer | string): Promise { + let fd = (await fs.open(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)).fd + let stat = await fs.stat(path) + await fs.write(fd, content, { offset: stat.size }) + await fs.close(fd) + } + + /** + * 异步拷贝文件 + * + * @param src 文件绝对路径及文件名 + * @param dest 拷贝到对应的路径 + */ + async copyFileAsync(src: string, dest: string): Promise { + await fs.copyFile(src, dest); + } + + /** + * 读取路径path的文件 + * + * @param path 文件绝对路径 + */ + async readFileAsync(path: string): Promise { + let stat = await fs.stat(path); + let fd = (await fs.open(path, fs.OpenMode.READ_WRITE)).fd; + let length = stat.size; + let buf = new ArrayBuffer(length); + await fs.read(fd, buf); + return buf + } + + /** + * 向path写入数据 + * + * @param path 文件绝对路径 + * @param data 文件内容 + */ + async writeNewFileAsync(path: string, data: ArrayBuffer | string): Promise { + let fd = (await fs.open(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)).fd + await fs.truncate(fd) + await fs.write(fd, data) + await fs.fsync(fd) + await fs.close(fd) + } uint8ArrayToBuffer(array: Uint8Array): ArrayBuffer { return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset) } + + /** + * 向path写入数据 + * + * @param path 文件绝对路径 + * @param data 文件内容 + */ + writeNewFile(path: string, data: ArrayBuffer | string) { + this.createFile(path) + this.writeFile(path, data) + } + + /** + * 读取路径path的文件 + * + * @param path 文件绝对路径 + */ + readFile(path: string): ArrayBuffer { + let fd = fs.openSync(path, fs.OpenMode.READ_WRITE).fd; + let length = fs.statSync(path).size + let buf = new ArrayBuffer(length); + fs.readSync(fd, buf) + return buf + } + } diff --git a/library/src/main/ets/components/imageknife/ImageKnife.ets b/library/src/main/ets/components/imageknife/ImageKnife.ets index 1c263cc..ccb262c 100644 --- a/library/src/main/ets/components/imageknife/ImageKnife.ets +++ b/library/src/main/ets/components/imageknife/ImageKnife.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { DiskLruCache } from "@ohos/disklrucache" +import { DiskLruCache } from "../cache/DiskLruCache" import { EngineKeyFactories } from "../cache/key/EngineKeyFactories" import { EngineKeyInterface } from "../cache/key/EngineKeyInterface" import { RequestOption } from "../imageknife/RequestOption" @@ -38,18 +38,30 @@ import { EasyLinkedHashMap } from './utils/base/EasyLinkedHashMap' import { MethodMutex } from './utils/base/MethodMutex' import worker from '@ohos.worker' import common from '@ohos.app.ability.common' -import HashMap from '@ohos.util.HashMap' -import LinkedList from '@ohos.util.LinkedList' import { MemoryLruCache } from '../cache/MemoryLruCache' +import { BusinessError } from '@kit.BasicServicesKit' +import { taskpool } from '@kit.ArkTS' +import { GIFFrame } from './utils/gif/GIFFrame' + +import { MemoryCacheProxy } from './requestmanage/MemoryCacheProxy' +import { ObjectKey } from './ObjectKey' + +export enum ResourceUsage { + + PLACEHOLDER = 'placeholder', + RETRYHODLER = 'retryholder', + ERRORHOLDER = 'errorholder', + MAIN = 'main' +} export class ImageKnife { static readonly SEPARATOR: string = '/' memoryCache: MemoryLruCache; - diskMemoryCache: DiskLruCache; dataFetch: IDataFetch; resourceFetch: IResourceFetch; filesPath: string = ""; // data/data/包名/files目录 - + diskMemoryCache: DiskLruCache; + memoryCacheProxy: MemoryCacheProxy = new MemoryCacheProxy(new MemoryLruCache(100, 100 * 1024 * 1024)); headerMap: Map = new Map(); //定义全局map placeholderCache: string = "placeholderCache" runningMaps: EasyLinkedHashMap; @@ -78,10 +90,7 @@ export class ImageKnife { this.pausedMaps = new EasyLinkedHashMap(); // 构造方法传入size 为保存文件个数 - this.memoryCache = new MemoryLruCache(100,100*1024*1024); - - // 创建disk缓存 传入的size 为多少比特 比如20KB 传入20*1024 - this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext()); + this.memoryCache = new MemoryLruCache(100, 100 * 1024 * 1024); // 创建网络下载能力 this.dataFetch = new DownloadClient(); @@ -92,13 +101,16 @@ export class ImageKnife { // 初始化本地 文件保存 this.filesPath = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).filesDir as string; + this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext); + // 通用文件格式识别初始化 this.fileTypeUtil = new FileTypeUtil(); this.engineKeyImpl = new EngineKeyFactories(); - } + //全局设置请求头调用方法 addHeader(key: string, value: Object) { this.headerMap.set(key, value); @@ -112,6 +124,10 @@ export class ImageKnife { return this.memoryCache; } + getMemoryCacheProxy(): MemoryCacheProxy { + return this.memoryCacheProxy; + } + public static with(context: Object): ImageKnifeGlobal { // 存入hapContext; let global: ImageKnifeGlobal = ImageKnifeGlobal.getInstance(); @@ -184,11 +200,11 @@ export class ImageKnife { // 设置缓存张数,缓存大小,单位字节 - public setLruCacheSize(size: number,memory:number) { + public setLruCacheSize(size: number, memory: number) { if (this.memoryCache.map.size() <= 0) { - this.memoryCache = new MemoryLruCache(size,memory); + this.memoryCache = new MemoryLruCache(size, memory); } else { - let newLruCache = new MemoryLruCache(size,memory); + let newLruCache = new MemoryLruCache(size, memory); this.memoryCache.foreachLruCache((value: ImageKnifeData, key: string, map: Object) => { newLruCache.put(key, value); }) @@ -200,12 +216,15 @@ export class ImageKnife { this.dataFetch = fetch; } + // 替代原来的DiskLruCache public replaceDiskLruCache(size: number) { if (this.diskMemoryCache.getCacheMap().size() <= 0) { - this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size); + this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext, size); } else { - let newDiskLruCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size); + let newDiskLruCache = DiskLruCache.create(ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext, size); this.diskMemoryCache.foreachDiskLruCache((value: string | ArrayBuffer, key: string, map: Object) => { newDiskLruCache.set(key, value); }) @@ -293,16 +312,40 @@ export class ImageKnife { // 首先执行占位图 解析任务 if (request.placeholderSrc) { - PlaceHolderManager.execute(request) + let loadKey = ''; + if (typeof request.placeholderSrc == 'string') { + loadKey = request.placeholderSrc; + } else { + loadKey = JSON.stringify(request.placeholderSrc); + } + let size = JSON.stringify(request.size); + request.placeholderCacheKey = this.generateCacheKey(loadKey, size, request.dontAnimateFlag, request.signature) + this.taskpoolLoadResource(request, ResourceUsage.PLACEHOLDER); } // 其次执行重试占位图 解析任务 if (request.retryholderSrc) { - RetryHolderManager.execute(request) + let loadKey = ''; + if (typeof request.retryholderSrc == 'string') { + loadKey = request.retryholderSrc; + } else { + loadKey = JSON.stringify(request.retryholderSrc); + } + let size = JSON.stringify(request.size); + request.retryholderCacheKey = this.generateCacheKey(loadKey, size, request.dontAnimateFlag, request.signature) + this.taskpoolLoadResource(request, ResourceUsage.RETRYHODLER); } // 最后解析错误占位图 if (request.errorholderSrc) { - ErrorHolderManager.execute(request) + let loadKey = ''; + if (typeof request.errorholderSrc == 'string') { + loadKey = request.errorholderSrc; + } else { + loadKey = JSON.stringify(request.errorholderSrc); + } + let size = JSON.stringify(request.size); + request.errorholderCacheKey = this.generateCacheKey(loadKey, size, request.dontAnimateFlag, request.signature) + this.taskpoolLoadResource(request, ResourceUsage.ERRORHOLDER); } return this.parseSource(request); } @@ -312,6 +355,7 @@ export class ImageKnife { let cacheKey: string; let transferKey: string; let dataKey: string; + //设置全局缓存key if (this.engineKeyImpl) { factories = this.engineKeyImpl; } else { @@ -343,13 +387,13 @@ export class ImageKnife { let signature = request.signature; - cacheKey = factories.generateMemoryCacheKey(loadKey,size,transformed,dontAnimateFlag,signature); + cacheKey = factories.generateMemoryCacheKey(loadKey, size, transformed, dontAnimateFlag, signature); // 生成磁盘缓存变换后数据key 变换后数据保存在磁盘 - transferKey = factories.generateTransformedDiskCacheKey(loadKey,size,transformed,dontAnimateFlag,signature); + transferKey = factories.generateTransformedDiskCacheKey(loadKey, size, transformed, dontAnimateFlag, signature); // 生成磁盘缓存源数据key 原始数据保存在磁盘 - dataKey = factories.generateOriginalDiskCacheKey(loadKey,signature); + dataKey = factories.generateOriginalDiskCacheKey(loadKey, signature); request.generateCacheKey = cacheKey; request.generateResourceKey = transferKey; @@ -358,6 +402,18 @@ export class ImageKnife { this.loadCacheManager(request); } + private generateCacheKey(loadkey: string, size: string, dontAnimateFlag: boolean, signature?: ObjectKey) { + let factories: EngineKeyInterface; + + //设置全局缓存key + if (this.engineKeyImpl) { + factories = this.engineKeyImpl; + } else { + factories = new EngineKeyFactories(); + } + return factories.generateMemoryCacheKey(loadkey, size, '', dontAnimateFlag, signature); + } + // 删除执行结束的running removeRunning(request: RequestOption) { if (this.isPaused) { @@ -374,12 +430,11 @@ export class ImageKnife { private keyEqualPendingToRun(nextPending: RequestOption) { - - this.pendingMaps.remove(nextPending.uuid) this.runningMaps.put(nextPending.uuid, nextPending); - RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch) + // RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch) + this.taskpoolLoadResource(nextPending, ResourceUsage.MAIN); } @@ -409,7 +464,8 @@ export class ImageKnife { let nextPending = pendingTailNode.value; this.runningMaps.put(nextPending.uuid, nextPending) this.pendingMaps.remove(nextPending.uuid) - RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch) + //RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch) + this.taskpoolLoadResource(nextPending, ResourceUsage.MAIN); } } @@ -427,7 +483,7 @@ export class ImageKnife { } if (hasEqualRunning) { - if(tailNode != null && tailNode.value != null) { + if (tailNode != null && tailNode.value != null) { this.keyEqualPendingToRun(tailNode.value); } } else { @@ -461,9 +517,7 @@ export class ImageKnife { } else { this.runningMaps.put(request.uuid, request) - - // 不存在相同key的 任务可以并行 - RequestManager.execute(request, this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch) + this.taskpoolLoadResource(request, ResourceUsage.MAIN); } } else { @@ -472,6 +526,99 @@ export class ImageKnife { } } + //多线程请求加载资源 + private taskpoolLoadResource(request: RequestOption, usageType: ResourceUsage) { + + if (usageType == "placeholder") { + // 先从内存中取值 + let cache = this.memoryCacheProxy.loadMemoryCache(request.placeholderCacheKey, true); + if (cache) { + LogUtil.info("imageknife load placeholder from MemoryCache") + request.placeholderOnComplete(cache); + return; + } + } else if (usageType == "retryholder") { + // 先从内存中取值 + let cache = this.memoryCacheProxy.loadMemoryCache(request.retryholderCacheKey, true); + if (cache) { + LogUtil.info("imageknife load retryholder from MemoryCache") + request.retryholderOnComplete(cache); + return; + } + } else if (usageType == "errorholder") { + let cache = this.memoryCacheProxy.loadMemoryCache(request.errorholderCacheKey, true); + if (cache) { + LogUtil.info("imageknife load errorholder from MemoryCache") + request.errorholderOnComplete(cache); + return; + } + } else { + // 先从内存中取值 + let cache = this.memoryCacheProxy.loadMemoryCache(request.generateCacheKey, request.isCacheable); + if (cache) { + LogUtil.info("imageknife load resource from MemoryCache") + cache.waitSaveDisk = false; + request.loadComplete(cache); + return; + } + } + //图片变换方法无法直接传递到子线程,这里先把对象名和构造参数传递到子线程,然后在子线程中实例化变换方法 + let transformations: string [][] = []; + if (usageType == ResourceUsage.MAIN) { + for (let i = 0; i < request.transformations.length; i++) { + transformations.push([request.transformations[i].getClassName(), request.transformations[i].getConstructorParams()]) + } + } + + //將要传递到子线程的参数,放在一个json对象上,避免方法参数过多 + let taskParams = JSON.stringify({ + transformations: transformations, + request: request, + usageType: usageType + }) + + //使用taskpool多线程执行资源下载 + let task = new taskpool.Task(taskExecute, taskParams, request.headers, request.moduleContext as common.UIAbilityContext) + task.setTransferList([]) + taskpool.execute(task).then((data) => { + if (usageType == "placeholder") { + if ((typeof (data as PixelMap).isEditable) == 'boolean') { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap); + request.placeholderOnComplete(imageKnifeData) + } else { + request.placeholderOnError("request placeholder error") + } + } else if (usageType == "retryholder") { + if ((typeof (data as PixelMap).isEditable) == 'boolean') { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap); + request.retryholderOnComplete(imageKnifeData) + } else { + request.retryholderOnError("request retryholder error") + } + } else if (usageType == "errorholder") { + if ((typeof (data as PixelMap).isEditable) == 'boolean') { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap); + request.errorholderOnComplete(imageKnifeData) + } else { + request.errorholderOnError("request errorholder error") + } + } else { + if ((typeof (data as PixelMap).isEditable) == 'boolean') { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap); + request.loadComplete(imageKnifeData) + } else if ((data as GIFFrame[]).length > 0) { + let imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, data as GIFFrame[]); + request.loadComplete(imageKnifeData) + } else { + request.loadError("request resources error") + } + } + }).catch((err: BusinessError | string) => { + request.loadError(err) + }) + + } + private keyNotEmpty(request: RequestOption): boolean { if ( request.generateCacheKey != null && request.generateCacheKey.length > 0 && @@ -521,11 +668,10 @@ export class ImageKnife { if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, request.loadSrc as PixelMap) request.loadComplete(imageKnifeData); - } else - if (typeof request.loadSrc == 'string') { - // 进入三级缓存模型 - return this.loadResources(request); - } else { + } else if (typeof request.loadSrc == 'string') { + // 进入三级缓存模型 + return this.loadResources(request); + } else { let res = request.loadSrc as Resource; if (typeof res.id != 'undefined' && typeof res.type != 'undefined') { // 进入三级缓存模型 本地资源不参与磁盘缓存 @@ -539,5 +685,77 @@ export class ImageKnife { } } +/** + * 加载资源子线程包含流程:网络请求资源->下载资源到本地->解码成ixelMap | GIFFrame[]->缓存到内存和磁盘 + * @param taskParams:任务参数,JSON字符串类型 + * @param headers:请求头 + * @param moduleContext:模块上下文 + * @returns + */ +@Concurrent +async function taskExecute(taskParams: string, headers: Map, moduleContext: common.UIAbilityContext): Promise { + // try { + let params: object = JSON.parse(taskParams); + let option = params["request"] as RequestOption; + let transformations = params["transformations"] as string [][]; + let usageType = params["usageType"] as string; + //子线程构造RequestOption对象 + let newRequestOption = new RequestOption(); + newRequestOption.uuid = option.uuid; + newRequestOption.loadSrc = option.loadSrc; + newRequestOption.dontAnimateFlag = option.dontAnimateFlag; + newRequestOption.generateCacheKey = option.generateCacheKey; + newRequestOption.generateResourceKey = option.generateResourceKey; + newRequestOption.generateDataKey = option.generateDataKey; + newRequestOption.thumbSizeMultiplier = option.thumbSizeMultiplier; + newRequestOption.thumbDelayTime = option.thumbDelayTime; + newRequestOption.size = option.size; + + newRequestOption.placeholderSrc = option.placeholderSrc; + newRequestOption.errorholderSrc = option.errorholderSrc; + newRequestOption.retryholderSrc = option.retryholderSrc; + newRequestOption.onlyRetrieveFromCache = option.onlyRetrieveFromCache; + newRequestOption.gpuEnabled = option.gpuEnabled; + newRequestOption.headers = headers; + newRequestOption.signature = option.signature; + ImageKnifeGlobal.getInstance().setHapContext(moduleContext); + newRequestOption.moduleContext = moduleContext; + if (option.isCacheable != null && option.isCacheable != undefined) { + newRequestOption.isCacheable = option.isCacheable; + } + //如果是本地图片不作磁盘缓存 + if (typeof option.loadSrc !== 'string') { + let none = new NONE(); + newRequestOption.diskCacheStrategy(none); + } + + if (usageType == "placeholder") { + let manager = new PlaceHolderManager(newRequestOption); + return await new Promise(manager.process); + } else if (usageType == "retryholder") { + let manager = new RetryHolderManager(newRequestOption); + return await new Promise(manager.process); + } else if (usageType == "errorholder") { + let manager = new ErrorHolderManager(newRequestOption); + return await new Promise(manager.process); + } else { + if (transformations) { + for (let i = 0; i < transformations.length; i++) { + let className = transformations[i][0] as string; + let params = transformations[i][1] as string; + newRequestOption.addTransformations(className, params); + } + } + let newDataFetch = new DownloadClient(); + let newResourceFetch = new ParseResClient(); + let manager = new RequestManager(newRequestOption, newDataFetch, newResourceFetch); + return await new Promise(manager.process); + } + // } catch (e) { + // console.log(e) + // return await new Promise(() => { + // }); + // } +} diff --git a/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets b/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets index 9381f99..dd3944d 100644 --- a/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets +++ b/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets @@ -241,20 +241,20 @@ export struct ImageKnifeComponent { } if (this.imageKnifeOption.placeholderSrc) { request.placeholder(this.imageKnifeOption.placeholderSrc, {asyncSuccess:(data:ImageKnifeData) => { - LogUtil.log('ImageKnifeComponent request.placeholder callback') + LogUtil.log('ImageKnife ImageKnifeComponent request.placeholder callback') this.runNextFunction(this.displayPlaceholder,data) } }) } if (this.imageKnifeOption.thumbSizeMultiplier) { request.thumbnail(this.imageKnifeOption.thumbSizeMultiplier, {asyncSuccess:(data:ImageKnifeData) => { - LogUtil.log('ImageKnifeComponent request.thumbnail callback') + LogUtil.log('ImageKnife ImageKnifeComponent request.thumbnail callback') this.runNextFunction(this.displayThumbSizeMultiplier,data) }}, this.imageKnifeOption.thumbSizeDelay) } if (this.imageKnifeOption.errorholderSrc) { request.errorholder(this.imageKnifeOption.errorholderSrc, {asyncSuccess:(data:ImageKnifeData) => { - LogUtil.log('ImageKnifeComponent request.errorholder callback') + LogUtil.log('ImageKnife ImageKnifeComponent request.errorholder callback') this.runNextFunction(this.displayErrorholder,data) }}) } @@ -276,14 +276,14 @@ export struct ImageKnifeComponent { if (this.imageKnifeOption.displayProgress) { request.addProgressListener({asyncSuccess:(percentValue: number) => { // 如果进度条百分比 未展示大小,展示其动画 - LogUtil.log('ImageKnifeComponent request.addProgressListener callback') + LogUtil.log('ImageKnife ImageKnifeComponent request.addProgressListener callback') this.runNextFunction(this.displayProgress,percentValue) }}) } if (this.imageKnifeOption.retryholderSrc) { request.retryholder(this.imageKnifeOption.retryholderSrc,{asyncSuccess: (data:ImageKnifeData) => { - LogUtil.log('ImageKnifeComponent request.retryholder callback') + LogUtil.log('ImageKnife ImageKnifeComponent request.retryholder callback') this.hasDisplayRetryholder = true this.runNextFunction(this.displayRetryholder,data) }}) @@ -323,7 +323,7 @@ export struct ImageKnifeComponent { this.configNecessary(request); this.configCacheStrategy(request); this.configDisplay(request); - this.configHspContext(request) + this.configHspContext(request); this.configRenderGpu(request); if(ImageKnifeGlobal.getInstance().getImageKnife()!=undefined) { ImageKnifeGlobal.getInstance().getImageKnife()?.call(request); diff --git a/library/src/main/ets/components/imageknife/RequestOption.ets b/library/src/main/ets/components/imageknife/RequestOption.ets index 50ecd2f..837bbf0 100644 --- a/library/src/main/ets/components/imageknife/RequestOption.ets +++ b/library/src/main/ets/components/imageknife/RequestOption.ets @@ -12,15 +12,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {ImageKnife} from './ImageKnife' +import { ImageKnife } from './ImageKnife' import { DiskStrategy } from "../cache/diskstrategy/DiskStrategy" -import { AsyncCallback } from "../imageknife/interface/AsyncCallback" -import { AsyncSuccess } from "../imageknife/interface/AsyncSuccess" -import { IAllCacheInfoCallback } from "../imageknife/interface/IAllCacheInfoCallback" +import { AsyncCallback } from "../imageknife/interface/AsyncCallback" +import { AsyncSuccess } from "../imageknife/interface/AsyncSuccess" +import { AllCacheInfo, IAllCacheInfoCallback } from "../imageknife/interface/IAllCacheInfoCallback" import { AUTOMATIC } from "../cache/diskstrategy/enum/AUTOMATIC" import { BaseTransform } from "../imageknife/transform/BaseTransform" import { RotateImageTransformation } from "../imageknife/transform/RotateImageTransformation" -import { ImageKnifeData } from "../imageknife/ImageKnifeData" +import { ImageKnifeData, ImageKnifeType } from "../imageknife/ImageKnifeData" import { CenterCrop } from '../imageknife/transform/pixelmap/CenterCrop' import { CenterInside } from '../imageknife/transform/pixelmap/CenterInside' import { FitCenter } from '../imageknife/transform/pixelmap/FitCenter' @@ -28,7 +28,10 @@ import { RoundedCornersTransformation, RoundCorner } from '../imageknife/transfo import { CropCircleTransformation } from '../imageknife/transform/CropCircleTransformation' -import { CropCircleWithBorderTransformation, rgbColor } from '../imageknife/transform/CropCircleWithBorderTransformation' +import { + CropCircleWithBorderTransformation, + rgbColor +} from '../imageknife/transform/CropCircleWithBorderTransformation' import { CropSquareTransformation } from '../imageknife/transform/CropSquareTransformation' import { CropTransformation } from '../imageknife/transform/CropTransformation' import { CropType } from '../imageknife/transform/CropTransformation' @@ -50,28 +53,37 @@ import { ImageKnifeGlobal } from './ImageKnifeGlobal' import { BusinessError } from '@ohos.base' import { ObjectKey } from './ObjectKey' import common from '@ohos.app.ability.common' +import { GIFFrame } from './utils/gif/GIFFrame' +import { MemoryCacheProxy } from './requestmanage/MemoryCacheProxy' +import { DiskCacheProxy } from './requestmanage/DiskCacheProxy' +import { DiskLruCache } from '../cache/disklrucache' +import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5' +import { FileUtils } from '../cache/FileUtils' export interface Size { width: number, height: number } -export interface DetachFromLayout{ - detach:()=>void + +export interface DetachFromLayout { + detach: () => void } export class RequestOption { // 遍历添加图片http请求头 headers: Map = new Map(); + // RequestOption调用header对于的方法 addHeader(key: string, value: Object) { this.headers.set(key, value); } + // 全局调用header对应的方法,包含RequestOption的形式 addHeaderMap(map: Map) { this.headers = map; } - uuid:string ='' // 唯一标识 + uuid: string = '' // 唯一标识 loadSrc: string | PixelMap | Resource = ''; strategy: DiskStrategy = new AUTOMATIC(); dontAnimateFlag = false; @@ -79,28 +91,24 @@ export class RequestOption { placeholderFunc: AsyncSuccess | undefined = undefined; errorholderSrc: PixelMap | Resource | undefined = undefined; errorholderFunc: AsyncSuccess | undefined = undefined; - errorholderData: ImageKnifeData | undefined = undefined;; + errorholderData: ImageKnifeData | undefined = undefined; + ; thumbSizeMultiplier: number = 0; - // 如果存在缩略图,则主图延时1s加载 thumbDelayTime: number = 1000 thumbHolderFunc: AsyncSuccess | undefined = undefined; requestListeners: Array> | undefined = undefined; - // 进度条 progressFunc: AsyncSuccess | undefined = undefined; - // 重试图层 retryholderSrc: PixelMap | Resource | undefined = undefined; retryholderFunc: AsyncSuccess | undefined = undefined; retryholderData: ImageKnifeData | undefined = undefined; - size:Size= { width: -1, height: -1 }; - + size: Size = { width: -1, height: -1 }; // 网络下载数据回调 allCacheInfoCallback: IAllCacheInfoCallback | undefined = undefined; onlyRetrieveFromCache: boolean = false; isCacheable: boolean = true; - // 开启GPU变换绘制 gpuEnabled: boolean = false; // 变换相关 @@ -110,40 +118,35 @@ export class RequestOption { generateDataKey: string = ""; filesPath: string = ""; // data/data/包名/files目录 cachesPath: string = ""; // 网络下载默认存储在data/data/包名/cache/ImageKnifeNetworkFolder/目标md5.img下面 - + placeholderCacheKey: string = ""; + retryholderCacheKey: string = ""; + errorholderCacheKey: string = ""; // 自定义缓存关键字 signature?: ObjectKey; - // 下载原始文件地址 downloadFilePath: string = ""; - // 网络文件下载统一存放 networkCacheFolder: string = "ImageKnifeNetworkFolder" - - // 主线图片 状态变化 是否加载完成 // 主图未加载成功 显示占位图 主图加载成功不展示占位图 loadMainReady = false; - // 失败占位图展示状态 当true 表示主图加载失败需要展示失败占位图 loadErrorReady = false; - // 重试占位图展示状态 当true 表示主图加载失败需要展示失败占位图 loadRetryReady = false; - // 缩略图展示 loadThumbnailReady = false; - - detachFromLayout:DetachFromLayout = { - detach: ()=>{ - let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); - if(imageKnife != undefined) { + detachFromLayout: DetachFromLayout = { + detach: () => { + let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + if (imageKnife != undefined) { imageKnife.remove(this.uuid); } } } // module资源的需要当前的module context - moduleContext?:common.UIAbilityContext = undefined; + moduleContext?: common.UIAbilityContext = undefined; + constructor() { // 初始化全局监听 this.requestListeners = new Array(); @@ -151,32 +154,34 @@ export class RequestOption { this.uuid = this.generateUUID(); - let ctx = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext - if(ctx != undefined){ + let ctx = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext + if (ctx != undefined) { this.moduleContext = ctx; } } generateUUID(): string { let d = new Date().getTime(); - const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( new RegExp("[xy]","g"), (c) => { + const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(new RegExp("[xy]", "g"), (c) => { const r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); }); return uuid; } - setModuleContext(moduleCtx:common.UIAbilityContext){ + + setModuleContext(moduleCtx: common.UIAbilityContext) { this.moduleContext = moduleCtx; } - getModuleContext():common.UIAbilityContext | undefined{ + getModuleContext(): common.UIAbilityContext | undefined { return this.moduleContext; } + /** * set image Component size */ - setImageViewSize(imageSize:Size) { + setImageViewSize(imageSize: Size) { this.size.width = imageSize.width; this.size.height = imageSize.height; return this; @@ -247,7 +252,7 @@ export class RequestOption { } addListener(func: AsyncCallback) { - if(this.requestListeners != undefined) { + if (this.requestListeners != undefined) { this.requestListeners?.push(func); } return this; @@ -267,8 +272,8 @@ export class RequestOption { this.onlyRetrieveFromCache = flag; } - rotateImage(degreesToRotate: number|undefined) { - if(degreesToRotate == undefined){ + rotateImage(degreesToRotate: number | undefined) { + if (degreesToRotate == undefined) { return } let rotateImage = new RotateImageTransformation(degreesToRotate); @@ -291,8 +296,8 @@ export class RequestOption { return this; } - roundedCorners(obj: RoundCorner|undefined) { - if(obj == undefined){ + roundedCorners(obj: RoundCorner | undefined) { + if (obj == undefined) { return } let transformation = new RoundedCornersTransformation({ @@ -311,8 +316,8 @@ export class RequestOption { return this; } - cropCircleWithBorder(border: number|undefined, obj: rgbColor|undefined) { - if(border == undefined || obj == undefined){ + cropCircleWithBorder(border: number | undefined, obj: rgbColor | undefined) { + if (border == undefined || obj == undefined) { return } let transformation = new CropCircleWithBorderTransformation(border, obj) @@ -326,8 +331,8 @@ export class RequestOption { return this; } - crop(width: number|undefined, height: number|undefined, cropType: CropType|undefined) { - if(width == undefined || height == undefined || cropType == undefined){ + crop(width: number | undefined, height: number | undefined, cropType: CropType | undefined) { + if (width == undefined || height == undefined || cropType == undefined) { return } let transformation = new CropTransformation(width, height, cropType) @@ -341,8 +346,8 @@ export class RequestOption { return this; } - brightnessFilter(brightness: number|undefined) { - if(brightness == undefined){ + brightnessFilter(brightness: number | undefined) { + if (brightness == undefined) { return } let transformation = new BrightnessFilterTransformation(brightness) @@ -350,8 +355,8 @@ export class RequestOption { return this; } - contrastFilter(contrast: number|undefined) { - if(contrast == undefined){ + contrastFilter(contrast: number | undefined) { + if (contrast == undefined) { return } let transformation = new ContrastFilterTransformation(contrast) @@ -377,22 +382,22 @@ export class RequestOption { return this; } - blur(radius: number|undefined , sampling?: number) { - if(radius == undefined){ + blur(radius: number | undefined, sampling?: number) { + if (radius == undefined) { return } - if(sampling == undefined) { + if (sampling == undefined) { let transformation = new BlurTransformation(radius) this.transformations.push(transformation); } else { - let transformation = new BlurTransformation(radius,sampling) + let transformation = new BlurTransformation(radius, sampling) this.transformations.push(transformation); } return this; } - pixelationFilter(pixel: number|undefined) { - if(pixel == undefined){ + pixelationFilter(pixel: number | undefined) { + if (pixel == undefined) { return } let transformation = new PixelationFilterTransformation(pixel) @@ -400,8 +405,8 @@ export class RequestOption { return this; } - swirlFilter(degree: number|undefined) { - if(degree == undefined){ + swirlFilter(degree: number | undefined) { + if (degree == undefined) { return } let transformation = new SwirlFilterTransformation(degree) @@ -409,8 +414,8 @@ export class RequestOption { return this; } - mask(maskResource: Resource|undefined) { - if(maskResource == undefined){ + mask(maskResource: Resource | undefined) { + if (maskResource == undefined) { return } let transformation = new MaskTransformation(maskResource) @@ -418,8 +423,8 @@ export class RequestOption { return this; } - kuwaharaFilter(radius: number|undefined) { - if(radius == undefined){ + kuwaharaFilter(radius: number | undefined) { + if (radius == undefined) { return } let transformation = new KuwaharaFilterTransform(radius); @@ -427,8 +432,8 @@ export class RequestOption { return this; } - toonFilter(threshold: number|undefined, quantizationLevels: number|undefined) { - if(threshold == undefined || quantizationLevels == undefined){ + toonFilter(threshold: number | undefined, quantizationLevels: number | undefined) { + if (threshold == undefined || quantizationLevels == undefined) { return } let transformation = new ToonFilterTransform(threshold, quantizationLevels); @@ -436,8 +441,8 @@ export class RequestOption { return this; } - vignetteFilter(centerPoint: Array|undefined, vignetteColor: Array|undefined, vignetteSpace: Array|undefined) { - if(centerPoint == undefined || vignetteColor == undefined || vignetteSpace == undefined){ + vignetteFilter(centerPoint: Array | undefined, vignetteColor: Array | undefined, vignetteSpace: Array | undefined) { + if (centerPoint == undefined || vignetteColor == undefined || vignetteSpace == undefined) { return } let transformation = new VignetteFilterTransform(centerPoint, vignetteColor, vignetteSpace); @@ -454,6 +459,7 @@ export class RequestOption { this.transformations = inputs; return this; } + // 开启GPU变换绘制 enableGPU() { this.gpuEnabled = true; @@ -461,72 +467,78 @@ export class RequestOption { } // 占位图解析成功 - placeholderOnComplete = (imageKnifeData: ImageKnifeData)=> { + placeholderOnComplete = (imageKnifeData:ImageKnifeData) => { LogUtil.log("placeholderOnComplete has called!"); LogUtil.log("Main Image is Ready:" + this.loadMainReady); + this.setMemoryCache(imageKnifeData,this.placeholderCacheKey); if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady) && !this.loadThumbnailReady) { // 主图未加载成功,并且未加载失败 显示占位图 主图加载成功或者加载失败后=>不展示占位图 - if(this.placeholderSrc != undefined) { + if (this.placeholderSrc != undefined) { this.placeholderFunc?.asyncSuccess(imageKnifeData) } } } - // 占位图解析失败 - placeholderOnError = (error:BusinessError|string)=>{ + placeholderOnError = (error: BusinessError | string) => { LogUtil.log("占位图解析失败 error =" + error) } - - // 缩略图解析成功 - thumbholderOnComplete = (imageKnifeData: ImageKnifeData)=> { + thumbholderOnComplete = (value: PixelMap | GIFFrame[]) => { + let imageKnifeData = new ImageKnifeData(); + if ((typeof (value as PixelMap).isEditable) == 'boolean') { + imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value as PixelMap); + } else { + imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, value as GIFFrame[]); + } if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady)) { // 主图未加载成功,并且未加载失败 显示占位图 主图加载成功或者加载失败后=>不展示占位图 - if(this.thumbHolderFunc != undefined) { + if (this.thumbHolderFunc != undefined) { this.thumbHolderFunc?.asyncSuccess(imageKnifeData) } } } - // 缩略图解析失败 - thumbholderOnError=(error? :BusinessError|string)=>{ + thumbholderOnError = (error?: BusinessError | string) => { LogUtil.log("缩略图解析失败 error =" + error) } - // 加载失败 占位图解析成功 - errorholderOnComplete = (imageKnifeData: ImageKnifeData)=> { + errorholderOnComplete = (imageKnifeData:ImageKnifeData) => { + this.setMemoryCache(imageKnifeData,this.errorholderCacheKey); // 如果有错误占位图 先解析并保存在RequestOption中 等到加载失败时候进行调用 this.errorholderData = imageKnifeData; if (this.loadErrorReady) { - if(this.errorholderFunc != undefined) { + if (this.errorholderFunc != undefined) { this.errorholderFunc.asyncSuccess(imageKnifeData) } } } - // 加载失败 占位图解析失败 - errorholderOnError = (error:BusinessError|string)=> { + errorholderOnError = (error: BusinessError | string) => { LogUtil.log("失败占位图解析失败 error =" + error) } - - retryholderOnComplete = (imageKnifeData: ImageKnifeData)=>{ + retryholderOnComplete = (imageKnifeData:ImageKnifeData) => { + this.setMemoryCache(imageKnifeData,this.retryholderCacheKey); this.retryholderData = imageKnifeData; if (this.loadRetryReady) { - if(this.retryholderFunc != undefined) { + if (this.retryholderFunc != undefined) { this.retryholderFunc?.asyncSuccess(imageKnifeData) } } } - - retryholderOnError = (error:BusinessError|string)=>{ + retryholderOnError = (error: BusinessError | string) => { LogUtil.log("重试占位图解析失败 error =" + error) } + loadComplete = async (imageKnifeData: ImageKnifeData) => { + this.setMemoryCache(imageKnifeData,this.generateCacheKey); + if (typeof this.loadSrc == 'string') { + this.setDiskCache(); + } - loadComplete = (imageKnifeData: ImageKnifeData)=>{ this.loadMainReady = true; + // 三级缓存数据加载成功 - if(this.requestListeners != undefined) { - for (let i = 0;i < this.requestListeners.length; i++) { + if (this.requestListeners != undefined) { + for (let i = 0; i < this.requestListeners.length; i++) { let requestListener = this.requestListeners[i]; let boolInterception = requestListener.callback("", imageKnifeData); if (boolInterception) { @@ -535,31 +547,87 @@ export class RequestOption { } } - if(imageKnifeData.waitSaveDisk){ + //显示进度条 + if (this.progressFunc) { + this.progressFunc.asyncSuccess(0); + setTimeout(() => { + }, 1000); + this.progressFunc.asyncSuccess(100); + } + + //输出缓存相关内容和信息 + if (this.allCacheInfoCallback) { + // 内存缓存 + let allCacheInfo: AllCacheInfo = { + memoryCacheInfo: { key: '', data: new ImageKnifeData() }, + resourceCacheInfo: { key: '', path: '' }, + dataCacheInfo: { key: '', path: '' } + }; + allCacheInfo.memoryCacheInfo = { + key: this.generateCacheKey, + data: imageKnifeData + } + let mDiskCacheProxy = new DiskCacheProxy(DiskLruCache.create(ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext)) + // 变换后缓存 + allCacheInfo.resourceCacheInfo = { + key: SparkMD5.hashBinary(this.generateResourceKey) as string, + path: (mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.generateResourceKey)) as string + }; + + // 原图缓存 + allCacheInfo.dataCacheInfo = { + key: SparkMD5.hashBinary(this.generateDataKey) as string, + path: (mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.generateDataKey)) as string + } + this.allCacheInfoCallback.callback(allCacheInfo); + } + + if (imageKnifeData.waitSaveDisk) { // 等落盘结束后主动调用#removeCurrentAndSearchNext方法 - }else{ + } else { // 非落盘情况,直接进行寻找下一个加载 - let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); - if(imageKnife != undefined) { + let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + if (imageKnife != undefined) { imageKnife.removeRunning(this); } } - - - - - - } + //设置内存缓存 + setMemoryCache = (imageKnifeData: ImageKnifeData,cacheKey:string) => { + let memoryCacheProxy = ImageKnifeGlobal.getInstance() + .getImageKnife()?.getMemoryCacheProxy() as MemoryCacheProxy; + memoryCacheProxy.putValue(cacheKey, imageKnifeData); + } + //设置磁盘缓存 + setDiskCache = () => { + try { + let diskMemoryCache = ImageKnifeGlobal.getInstance().getImageKnife()?.getDiskMemoryCache(); + let dataArraybuffer: ArrayBuffer = DiskLruCache.getFileCacheByFile((ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext).filesDir as string, this.generateDataKey) as ArrayBuffer; + //缓存原图片 + if (dataArraybuffer && !diskMemoryCache?.get(this.generateDataKey)) { + diskMemoryCache?.setCacheMapAndSize(this.generateDataKey, dataArraybuffer); + } + //缓存变换后图片 + let resourceArraybuffer: ArrayBuffer = DiskLruCache.getFileCacheByFile((ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext).filesDir as string, this.generateResourceKey) as ArrayBuffer; + if (resourceArraybuffer && !diskMemoryCache?.get(this.generateResourceKey)) { + diskMemoryCache?.setCacheMapAndSize(this.generateResourceKey, resourceArraybuffer); + } + + } catch (e) { + LogUtil.error("imageknife DiskMemoryCache setDiskCache error :" + e.message); + } + } // 图片文件落盘之后会自动去寻找下一个数据加载 - removeCurrentAndSearchNext =()=>{ - if(ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { + removeCurrentAndSearchNext = () => { + if (ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { (ImageKnifeGlobal.getInstance().getImageKnife())?.removeRunning(this); } } - - loadError = (err:BusinessError|string)=>{ + loadError = (err: BusinessError | string) => { LogUtil.log("loadError:" + err); // 失败占位图展示规则 if (this.retryholderFunc) { @@ -572,16 +640,105 @@ export class RequestOption { // 失败图层标记,如果已经有数据直接展示失败图层 this.loadErrorReady = true; if (this.errorholderData != null) { - if(this.errorholderFunc != undefined) { + if (this.errorholderFunc != undefined) { this.errorholderFunc.asyncSuccess(this.errorholderData) } } } // 加载失败之后 - if(ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { + if (ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { (ImageKnifeGlobal.getInstance().getImageKnife())?.removeRunning(this); } } + addTransformations = (transformationName: string, params: string) => { + switch (transformationName) { + case "BlurTransformation": + let paramList: number [] = JSON.parse(params); + this.transformations.push(new BlurTransformation(paramList[0], paramList[1])); + break; + case "BrightnessFilterTransformation": + let paramList1: number [] = JSON.parse(params); + this.transformations.push(new BrightnessFilterTransformation(paramList1[0])); + break; + case "RotateImageTransformation": + let paramList2: number [] = JSON.parse(params); + this.transformations.push(new RotateImageTransformation(paramList2[0])); + break; + case "GrayscaleTransformation": + this.transformations.push(new GrayscaleTransformation()); + break; + case "BlurTransformation": + let paramList3: number [] = JSON.parse(params); + this.transformations.push(new BlurTransformation(paramList3[0])); + break; + case "ContrastFilterTransformation": + let paramList4: number [] = JSON.parse(params); + this.transformations.push(new ContrastFilterTransformation(paramList4[0])); + break; + case "CropCircleTransformation": + this.transformations.push(new CropCircleTransformation()); + break; + case "CropCircleWithBorderTransformation": + let paramList5: (number | rgbColor) [] = JSON.parse(params); + this.transformations.push(new CropCircleWithBorderTransformation(paramList5[0] as number, paramList5[1] as rgbColor)); + break; + case "CropSquareTransformation": + this.transformations.push(new CropSquareTransformation()); + break; + case "InvertFilterTransformation": + this.transformations.push(new InvertFilterTransformation()); + break; + case "KuwaharaFilterTransform": + let paramList7: number [] = JSON.parse(params); + this.transformations.push(new KuwaharaFilterTransform(paramList7[0] as number)); + break; + case "MaskTransformation": + let paramList8: Resource [] = JSON.parse(params); + this.transformations.push(new MaskTransformation(paramList8[0] as Resource)); + break; + case "PixelationFilterTransformation": + let paramList9: number [] = JSON.parse(params); + this.transformations.push(new PixelationFilterTransformation(paramList9[0] as number)); + break; + case "RoundedCornersTransformation": + let paramList10: RoundCorner [] = JSON.parse(params); + this.transformations.push(new RoundedCornersTransformation(paramList10[0] as RoundCorner)); + break; + case "SepiaFilterTransformation": + this.transformations.push(new SepiaFilterTransformation()); + break; + case "SketchFilterTransformation": + this.transformations.push(new SketchFilterTransformation()); + break; + case "SwirlFilterTransformation": + let paramList11: (number | Array) [] = JSON.parse(params); + this.transformations.push(new SwirlFilterTransformation(paramList11[0] as number, paramList11[1] as number, paramList11[2] as Array)); + break; + case "ToonFilterTransform": + let paramList14: number [] = JSON.parse(params); + this.transformations.push(new ToonFilterTransform(paramList14[0], paramList14[1])); + break; + case "VignetteFilterTransform": + let paramList12: Array [] = JSON.parse(params); + this.transformations.push(new VignetteFilterTransform(paramList12[0], paramList12[1], paramList12[2])); + break; + case "CenterCrop": + this.transformations.push(new CenterCrop()); + break; + case "CenterInside": + this.transformations.push(new CenterInside()); + break; + case "FitCenter": + this.transformations.push(new FitCenter()); + break; + case "CropTransformation": + let paramList13: (number | CropType) [] = JSON.parse(params); + this.transformations.push(new CropTransformation(paramList13[0] as number, paramList13[1] as number, paramList13[2] as CropType)); + break; + default: + break + } + } } diff --git a/library/src/main/ets/components/imageknife/holder/ErrorHolderManager.ets b/library/src/main/ets/components/imageknife/holder/ErrorHolderManager.ets index 885f67c..6b460aa 100644 --- a/library/src/main/ets/components/imageknife/holder/ErrorHolderManager.ets +++ b/library/src/main/ets/components/imageknife/holder/ErrorHolderManager.ets @@ -31,21 +31,15 @@ export class ErrorHolderManager { this.options = option; } - static execute(option: RequestOption) { - let manager:ErrorHolderManager = new ErrorHolderManager(option); - return new Promise(manager.process) - .then(option.errorholderOnComplete).catch(option.errorholderOnError); - } - - process = (onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ + process = (onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ this.displayErrorholder(onComplete, onError); } - private displayErrorholder(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { + private displayErrorholder(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { LogUtil.log("displayErrorholder") if ((typeof (this.options.errorholderSrc as image.PixelMap).isEditable) == 'boolean') { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, this.options.errorholderSrc as PixelMap) - onComplete(imageKnifeData); + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap); } else if (typeof this.options.errorholderSrc == 'string') { } else { @@ -89,16 +83,16 @@ export class ErrorHolderManager { } } - private svgProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private svgProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let svgParseImpl:SVGParseImpl = new SVGParseImpl() svgParseImpl.parseSvg(this.options,arraybuffer, onComplete,onError); } - private mediaImageProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private mediaImageProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let parseImageUtil = new ParseImageUtil() let success = (value: PixelMap) => { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value) - onComplete(imageKnifeData) + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap) } parseImageUtil.parseImage(arraybuffer, success, onError) } diff --git a/library/src/main/ets/components/imageknife/holder/PlaceHolderManager.ets b/library/src/main/ets/components/imageknife/holder/PlaceHolderManager.ets index ad98cb5..651fec1 100644 --- a/library/src/main/ets/components/imageknife/holder/PlaceHolderManager.ets +++ b/library/src/main/ets/components/imageknife/holder/PlaceHolderManager.ets @@ -25,6 +25,8 @@ import {LogUtil} from '../../imageknife/utils/LogUtil' import resourceManager from '@ohos.resourceManager'; import image from "@ohos.multimedia.image" import { BusinessError } from '@ohos.base' +import { GIFFrame } from '../utils/gif/GIFFrame' + export class PlaceHolderManager { private options: RequestOption; @@ -32,21 +34,16 @@ export class PlaceHolderManager { this.options = option; } - static execute(option: RequestOption) { - let manager:PlaceHolderManager = new PlaceHolderManager(option); - return new Promise(manager.process) - .then(option.placeholderOnComplete).catch(option.placeholderOnError); - } - process = (onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ + process = (onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ this.displayPlaceholder(onComplete, onError); } - private displayPlaceholder(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void){ - LogUtil.log("displayPlaceholder") + private displayPlaceholder(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void){ + LogUtil.log("ImageKnife displayPlaceholder") if ((typeof (this.options.placeholderSrc as image.PixelMap).isEditable) == 'boolean') { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, this.options.placeholderSrc as PixelMap) - onComplete(imageKnifeData); + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap); } else if (typeof this.options.placeholderSrc == 'string') { } else { @@ -87,16 +84,16 @@ export class PlaceHolderManager { - private svgProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private svgProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let svgParseImpl:SVGParseImpl = new SVGParseImpl() svgParseImpl.parseSvg(this.options,arraybuffer, onComplete,onError); } - private mediaImageProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private mediaImageProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let parseImageUtil:ParseImageUtil = new ParseImageUtil() let success = (value: PixelMap) => { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value) - onComplete(imageKnifeData) + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap) } parseImageUtil.parseImage(arraybuffer, success, onError) } diff --git a/library/src/main/ets/components/imageknife/holder/RetryHolderManager.ets b/library/src/main/ets/components/imageknife/holder/RetryHolderManager.ets index d499f7a..8acfc20 100644 --- a/library/src/main/ets/components/imageknife/holder/RetryHolderManager.ets +++ b/library/src/main/ets/components/imageknife/holder/RetryHolderManager.ets @@ -32,21 +32,15 @@ export class RetryHolderManager { this.options = option; } - static execute(option: RequestOption) { - let manager:RetryHolderManager = new RetryHolderManager(option); - return new Promise(manager.process) - .then(option.retryholderOnComplete).catch(option.retryholderOnError); - } - - process = (onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ + process = (onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ this.displayRetryholder(onComplete, onError); } - private displayRetryholder(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void){ - LogUtil.log("displayRetryholder") + private displayRetryholder(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void){ + LogUtil.log("ImageKnife displayRetryholder") if ((typeof (this.options.retryholderSrc as image.PixelMap).isEditable) == 'boolean') { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, this.options.placeholderSrc as PixelMap) - onComplete(imageKnifeData); + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap); } else if (typeof this.options.placeholderSrc == 'string') { } else { @@ -87,16 +81,16 @@ export class RetryHolderManager { - private svgProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private svgProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let svgParseImpl = new SVGParseImpl() svgParseImpl.parseSvg(this.options,arraybuffer, onComplete,onError); } - private mediaImageProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private mediaImageProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let parseImageUtil = new ParseImageUtil() let success = (value: PixelMap) => { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value) - onComplete(imageKnifeData) + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap) } parseImageUtil.parseImage(arraybuffer, success, onError) } diff --git a/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets b/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets index e4c9a21..80b4a03 100644 --- a/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets +++ b/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets @@ -30,7 +30,7 @@ class RequestData{ } export class HttpDownloadClient implements IDataFetch { - loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) { + async loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) { try { let httpRequest = http.createHttp() let arrayBuffers = new Array(); @@ -64,18 +64,16 @@ export class HttpDownloadClient implements IDataFetch { request.headers.forEach((value, key) => { headerObj[key] = value }) - httpRequest.requestInStream( - request.loadSrc as string, - { - header: headerObj, - method: http.RequestMethod.GET, - expectDataType: http.HttpDataType.ARRAY_BUFFER, - connectTimeout: 60000, // 可选 默认60000ms - readTimeout: 0, // 可选, 默认为60000ms - usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定 - usingCache: false - } - ).then((data)=>{ + let promise = httpRequest.requestInStream(request.loadSrc as string, { + header: headerObj, + method: http.RequestMethod.GET, + expectDataType: http.HttpDataType.ARRAY_BUFFER, + connectTimeout: 60000, // 可选 默认60000ms + readTimeout: 0, // 可选, 默认为60000ms + usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定 + usingCache: false + }); + await promise.then((data)=>{ if(data == 200) { } else { diff --git a/library/src/main/ets/components/imageknife/requestmanage/DiskCacheProxy.ets b/library/src/main/ets/components/imageknife/requestmanage/DiskCacheProxy.ets index 62816df..efffcfe 100644 --- a/library/src/main/ets/components/imageknife/requestmanage/DiskCacheProxy.ets +++ b/library/src/main/ets/components/imageknife/requestmanage/DiskCacheProxy.ets @@ -14,7 +14,7 @@ */ import { ICache } from "../requestmanage/ICache" -import { DiskLruCache } from "@ohos/disklrucache" +import { DiskLruCache } from "../../cache/disklrucache" export class DiskCacheProxy implements ICache { private mDiskLruCache: DiskLruCache; @@ -38,7 +38,7 @@ export class DiskCacheProxy implements ICache { } getValue(key: string): ArrayBuffer{ - return this.mDiskLruCache.get(key); + return this.mDiskLruCache.get(key) as ArrayBuffer; } putValue(key: string, value: ArrayBuffer) { diff --git a/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets b/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets index 0854630..955cede 100644 --- a/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets +++ b/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets @@ -13,25 +13,26 @@ * limitations under the License. */ -import { RequestOption,Size } from '../../imageknife/RequestOption' -import { DiskLruCache } from '@ohos/disklrucache' +import { RequestOption, Size } from '../../imageknife/RequestOption' +import { DiskLruCache } from "../../cache/DiskLruCache" import { LruCache } from '../../cache/LruCache' import { SparkMD5 } from '../../3rd_party/sparkmd5/spark-md5' import { MemoryCacheProxy } from '../requestmanage/MemoryCacheProxy' import { DiskCacheProxy } from '../requestmanage/DiskCacheProxy' import { FileTypeUtil } from '../utils/FileTypeUtil' -import { IDataFetch } from '../../imageknife/networkmanage/IDataFetch' -import { IResourceFetch } from '../../imageknife/resourcemanage/IResourceFetch' +import { IDataFetch } from '../../imageknife/networkmanage/IDataFetch' +import { IResourceFetch } from '../../imageknife/resourcemanage/IResourceFetch' import { ImageKnifeData, ImageKnifeType } from '../ImageKnifeData' import { AllCacheInfo } from '../../imageknife/interface/IAllCacheInfoCallback' import { ParseImageUtil } from '../utils/ParseImageUtil' -import { IParseImage } from '../interface/IParseImage' +import { IParseImage } from '../interface/IParseImage' import image from '@ohos.multimedia.image' import { SVGParseImpl } from '../utils/svg/SVGParseImpl' import { GIFParseImpl } from '../utils/gif/GIFParseImpl' import { GIFFrame } from '../utils/gif/GIFFrame' import { LogUtil } from '../../imageknife/utils/LogUtil' import { BusinessError } from '@ohos.base' +import taskpool from '@ohos.taskpool'; export enum Stage { @@ -62,19 +63,13 @@ export enum RunReason { export class RequestManager { private TAG: string = "RequestManager"; private options: RequestOption; - private mMemoryCacheProxy: MemoryCacheProxy; - private mDiskCacheProxy: DiskCacheProxy; private mIDataFetch: IDataFetch; private mIResourceFetch: IResourceFetch; private mParseImageUtil: IParseImage; - constructor(option: RequestOption, memoryCache1: LruCache, diskMemoryCache1: DiskLruCache, dataFetch: IDataFetch, resourceFetch: IResourceFetch) { + constructor(option: RequestOption, dataFetch: IDataFetch, resourceFetch: IResourceFetch) { this.options = option; - // 缓存部分 - this.mMemoryCacheProxy = new MemoryCacheProxy(memoryCache1); - this.mDiskCacheProxy = new DiskCacheProxy(diskMemoryCache1); - // 网络下载能力 this.mIDataFetch = dataFetch; @@ -85,63 +80,16 @@ export class RequestManager { this.mParseImageUtil = new ParseImageUtil(); } - static execute(option: RequestOption, memoryCache1: LruCache, diskMemoryCache1: DiskLruCache, dataFetch: IDataFetch, resourceFetch: IResourceFetch) { - LogUtil.log("RequestManager execute") - let manager = new RequestManager(option, memoryCache1, diskMemoryCache1, dataFetch, resourceFetch); - return new Promise(manager.process) - .then(option.loadComplete) - .then(manager.loadCompleteAfter) - .catch(option.loadError); - } - - loadCompleteAfter =()=>{ - try { // 内部消化问题 - LogUtil.log("loadCompleteAfter!") - if (this.options.allCacheInfoCallback) { - LogUtil.log("RequestOption =" + JSON.stringify(this.options)); - - // 内存缓存 - let allCacheInfo:AllCacheInfo = { - memoryCacheInfo:{key:'', data:new ImageKnifeData()}, - resourceCacheInfo:{key:'', path:''}, - dataCacheInfo:{key:'',path:''} - }; - let memoryCache = this.mMemoryCacheProxy.getValue(this.options.generateCacheKey); - allCacheInfo.memoryCacheInfo = { - key: this.options.generateCacheKey, - data: memoryCache - } - - // 变换后缓存 - allCacheInfo.resourceCacheInfo = { - key: SparkMD5.hashBinary(this.options.generateResourceKey) as string, - path: (this.mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.options.generateResourceKey)) as string - }; - - // 原图缓存 - allCacheInfo.dataCacheInfo = { - key: SparkMD5.hashBinary(this.options.generateDataKey) as string, - path: (this.mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.options.generateDataKey)) as string - } - this.options.allCacheInfoCallback.callback(allCacheInfo) - } - } catch (err) { - LogUtil.log("after err =" + err) - } - } - - // DecodeJob work private mStage: Stage = Stage.INITIALIZE; private mRunReason: RunReason = RunReason.INITIALIZE; - - process = (onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ - LogUtil.log("RequestManager process !"); + process = (onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) => { + LogUtil.log("ImageKnife RequestManager process !"); this.loadLeve1MemoryCache(onComplete, onError) } - private runWrapped(request: RequestOption, runReason: RunReason, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager runWrapped") + private runWrapped(request: RequestOption, runReason: RunReason, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager runWrapped") if (runReason == RunReason.INITIALIZE) { this.mStage = this.getNextStage(request, this.mStage); this.searchLoadFrom(this.options, this.mStage, onComplete, onError); @@ -150,7 +98,7 @@ export class RequestManager { } } - private getNextStage(request: RequestOption, current: Stage): Stage{ + private getNextStage(request: RequestOption, current: Stage): Stage { if (current == Stage.INITIALIZE) { return request.strategy.decodeCachedResource() ? Stage.RESOURCE_CACHE @@ -166,13 +114,13 @@ export class RequestManager { } else if (current == Stage.FINISHED) { return Stage.FINISHED; } else { - throw new Error("Unrecognized stage: " + current); + throw new Error("ImageKnife Unrecognized stage: " + current); } } // 究竟从哪里加载数据 - private searchLoadFrom(request: RequestOption, current: Stage, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager searchLoadFrom") + private searchLoadFrom(request: RequestOption, current: Stage, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager searchLoadFrom") if (current == Stage.RESOURCE_CACHE) { this.loadDiskFromTransform(request, onComplete, onError); } else if (current == Stage.DATA_CACHE) { @@ -187,73 +135,73 @@ export class RequestManager { } // 加载网络资源 - private loadSourceFromNetwork(request: RequestOption, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - let success = (arraybuffer:ArrayBuffer) => { - this.downloadSuccess(request,arraybuffer, onComplete, onError) + private loadSourceFromNetwork(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + try { + LogUtil.log("ImageKnife RequestManager loadSourceFromNetwork") + let success = (arraybuffer: ArrayBuffer) => { + this.downloadSuccess(request, arraybuffer, onComplete, onError) } - let error = (errorMsg:string) =>{ + let error = (errorMsg: string) => { onError(errorMsg) } this.mIDataFetch.loadData(request, success, error); + } catch (e) { + LogUtil.error("ImageKnife RequestManager loadSourceFromNetwork error") + } } // 加载本地资源 - private loadSourceFormNative(request: RequestOption, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager loadSourceFormNative") + private loadSourceFormNative(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager loadSourceFormNative") // 本地解析后进行一级缓存 - let success = (arrayBuffer:ArrayBuffer) => { + let success = (arrayBuffer: ArrayBuffer) => { // 使用媒体子系统 ImageSource解析文件 获取PixelMap let fileTypeUtil = new FileTypeUtil(); let typeValue = fileTypeUtil.getFileType(arrayBuffer) - LogUtil.log("RequestManager - 文件类型为= " + typeValue) + LogUtil.log("ImageKnife RequestManager - 文件类型为= " + typeValue) // gif处理 - if(ImageKnifeData.GIF == typeValue && !request.dontAnimateFlag){ + if (ImageKnifeData.GIF == typeValue && !request.dontAnimateFlag) { // 处理gif - this.gifProcess(onComplete,onError, arrayBuffer,typeValue,(imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData) - }) - }else if(ImageKnifeData.SVG == typeValue){ + this.gifProcess(onComplete, onError, arrayBuffer, typeValue) + } else if (ImageKnifeData.SVG == typeValue) { // 处理svg - this.svgProcess(request,onComplete,onError,arrayBuffer,typeValue,(imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey,imageKnifeData) - }) + this.svgProcess(request, onComplete, onError, arrayBuffer, typeValue) } else { if (request.transformations[0]) { - request.transformations[0].transform(arrayBuffer, request, {asyncTransform:(error:BusinessError|string, pixelMap: PixelMap|null) => { - // 输出给Image - if (pixelMap) { - - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap); - this.mMemoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); - } else { - onError(error); + request.transformations[0].transform(arrayBuffer, request, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + // 输出给Image + if (pixelMap) { + onComplete(pixelMap); + } else { + onError(error); + } } - }}) + }) } else { let success = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - this.mMemoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); + onComplete(value); } this.mParseImageUtil.parseImage(arrayBuffer, success, onError) } } } let ctx = request.getModuleContext(); - if(ctx!=undefined){ - this.mIResourceFetch.loadResource(ctx,request.loadSrc as Resource, success, onError); - }else{ - onError('RequestManager loadSourceFormNative moduleContext is undefined! please check it') + if (ctx != undefined) { + this.mIResourceFetch.loadResource(ctx, request.loadSrc as Resource, success, onError); + } else { + onError('ImageKnife RequestManager loadSourceFormNative moduleContext is undefined! please check it') } } + // 加载磁盘缓存 原图 - private loadDiskFromSource(request: RequestOption, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager loadDiskFromSource") - let cached = this.mDiskCacheProxy.getValue(request.generateDataKey) + private loadDiskFromSource(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager loadDiskFromSource") + let cached = DiskLruCache.getFileCacheByFile(request?.moduleContext?.filesDir as string, request.generateDataKey); if (cached != null && cached.byteLength > 0) { + LogUtil.log("ImageKnife loadDiskFromSource load resource from DiskLruCache") this.parseDiskFile2PixelMap(request, cached, onComplete, onError) } else { this.mStage = Stage.SOURCE; @@ -262,10 +210,11 @@ export class RequestManager { } // 加载磁盘缓存 变换后图片 - private loadDiskFromTransform(request: RequestOption, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager loadDiskFromTransform") - let cached = this.mDiskCacheProxy.getValue(request.generateResourceKey) + private loadDiskFromTransform(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager loadDiskFromTransform") + let cached = DiskLruCache.getFileCacheByFile(request.moduleContext?.filesDir as string, request.generateResourceKey); if (cached != null) { + LogUtil.log("ImageKnife loadDiskFromTransform load resource from DiskLruCache") this.parseDiskTransformFile2PixelMap(request, cached, onComplete, onError) } else { this.mStage = Stage.DATA_CACHE; @@ -273,103 +222,95 @@ export class RequestManager { } } - parseSource(request: RequestOption, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager parseSource") + parseSource(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager parseSource") + try { if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') { // PixelMap 外层捕获效率更高,不会进入这里 } else if (typeof request.loadSrc == 'string') { this.loadSourceFromNetwork(request, onComplete, onError); } else { let res = request.loadSrc as Resource; - if (typeof res.id != 'undefined' && typeof res.id != 'undefined') { + if (typeof res.id != 'undefined') { this.loadSourceFormNative(request, onComplete, onError) } else { LogUtil.log("输入参数有问题!") } } + } catch (e) { + LogUtil.error("ImageKnife RequestManager parseSource error") + } } - private loadLeve1MemoryCache(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager loadLeve1MemoryCache") - // 一级缓存 内存获取 - let cache = this.mMemoryCacheProxy.loadMemoryCache(this.options.generateCacheKey, this.options.isCacheable); - if (cache == null || typeof cache == 'undefined') { - // 处理磁盘加载 网络加载 + private loadLeve1MemoryCache(onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + try { this.runWrapped(this.options, this.mRunReason, onComplete, onError) - } else { - // 需要清理状态 - cache.waitSaveDisk = false; - onComplete(cache); - return - + } catch (e) { + LogUtil.error("ImageKnife loadLeve1MemoryCache error") } } // 解析磁盘文件变成PixeMap - private parseDiskFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { + private parseDiskFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager parseDiskFile2PixelMap") // 步骤一:文件转为pixelMap 然后变换 给Image组件 let fileTypeUtil = new FileTypeUtil(); let typeValue = fileTypeUtil.getFileType(source); // 解析磁盘文件 gif 和 svg - if(ImageKnifeData.GIF == typeValue && !request.dontAnimateFlag){ + if (ImageKnifeData.GIF == typeValue && !request.dontAnimateFlag) { // 处理gif - this.gifProcess(onComplete,onError,source,typeValue, (imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData) - }) - }else if(ImageKnifeData.SVG == typeValue){ - this.svgProcess(request,onComplete,onError, source, typeValue, (imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData) - }) + this.gifProcess(onComplete, onError, source, typeValue) + } else if (ImageKnifeData.SVG == typeValue) { + this.svgProcess(request, onComplete, onError, source, typeValue) } else { if (this.options.transformations[0]) { if (this.options.thumbSizeMultiplier) { - let thumbOption:RequestOption = new RequestOption(); + let thumbOption: RequestOption = new RequestOption(); thumbOption.setImageViewSize({ width: Math.round(this.options.thumbSizeMultiplier * this.options.size.width), height: Math.round(this.options.thumbSizeMultiplier * this.options.size.height) }) let ctx = this.options.getModuleContext() - if(ctx != undefined){ + if (ctx != undefined) { thumbOption.setModuleContext(ctx) - }else{ + } else { onError('RequestManager parseDiskFile2PixelMap moduleContext is undefined, please check it!') return } let thumbCallback = this.options.thumbholderOnComplete; let thumbError = this.options.thumbholderOnError; - this.options.transformations[0].transform(source, thumbOption,{asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { - if (pixelMap) { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap); - thumbCallback(imageKnifeData); - } else { - thumbError(error); - } - }}) - setTimeout(()=>{ - this.options.transformations[0].transform(source, request, {asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { + this.options.transformations[0].transform(source, thumbOption, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { if (pixelMap) { - // 保存一份变换后的图片PixelMap到MemoryCache - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); + thumbCallback(pixelMap); + } else { + thumbError(error); + } + } + }) + setTimeout(() => { + this.options.transformations[0].transform(source, request, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + onComplete(pixelMap); + } else { + onError(error); + } + } + }) + }, this.options.thumbDelayTime); + } + else { + this.options.transformations[0].transform(source, request, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + onComplete(pixelMap); } else { onError(error); } - }}) - },this.options.thumbDelayTime); - } - else { - this.options.transformations[0].transform(source, request, {asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { - if (pixelMap) { - // 保存一份变换后的图片PixelMap到MemoryCache - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); - } else { - onError(error); } - }}) + }) } } else { // thumbnail 缩略图部分 @@ -377,24 +318,18 @@ export class RequestManager { let thumbCallback = this.options.thumbholderOnComplete let thumbError = this.options.thumbholderOnError let thumbSuccess = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - thumbCallback(imageKnifeData); + thumbCallback(value); } this.mParseImageUtil.parseImageThumbnail(request.thumbSizeMultiplier, source, thumbSuccess, thumbError); - setTimeout(()=>{ + setTimeout(() => { let success = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); - } - this.mParseImageUtil.parseImage(source, success, onError) - },this.options.thumbDelayTime) - } - else { + onComplete(value); + } + this.mParseImageUtil.parseImage(source, success, onError) + }, this.options.thumbDelayTime) + } else { let success = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); + onComplete(value); } this.mParseImageUtil.parseImage(source, success, onError) } @@ -403,7 +338,8 @@ export class RequestManager { } // 解析磁盘变换后文件变成PixeMap - private parseDiskTransformFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { + private parseDiskTransformFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager parseDiskTransformFile2PixelMap") let fileTypeUtil = new FileTypeUtil(); let typeValue = fileTypeUtil.getFileType(source); // thumbnail 缩略图部分 @@ -411,33 +347,27 @@ export class RequestManager { let thumbCallback = this.options.thumbholderOnComplete let thumbError = this.options.thumbholderOnError let thumbSuccess = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - thumbCallback(imageKnifeData); + thumbCallback(value); } this.mParseImageUtil.parseImageThumbnail(request.thumbSizeMultiplier, source, thumbSuccess, thumbError); - setTimeout(()=>{ + setTimeout(() => { let success = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); + onComplete(value); } this.mParseImageUtil.parseImage(source, success, onError) - },this.options.thumbDelayTime) - }else{ + }, this.options.thumbDelayTime) + } else { let success = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value) - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); + onComplete(value); } this.mParseImageUtil.parseImage(source, success, onError) } } - private downloadSuccess(request: RequestOption,source: ArrayBuffer, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log('Download task completed.'); + private downloadSuccess(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - if(source == null || source == undefined || source.byteLength <= 0){ - onError('Download task completed. but download file is empty!') + if (source == null || source == undefined || source.byteLength <= 0) { + onError('ImageKnife Download task completed. but download file is empty!') return } @@ -446,8 +376,8 @@ export class RequestManager { // 步骤二: 文件名保存一份全局 // 步骤三:查看文件是否支持 非支持类型直接返回 let fileTypeUtil = new FileTypeUtil(); - let filetype:string|null = fileTypeUtil.getFileType(source); - if(filetype == null){ + let filetype: string | null = fileTypeUtil.getFileType(source); + if (filetype == null) { onError("下载文件解析后类型为null,请检查数据源!"); return; } @@ -458,52 +388,50 @@ export class RequestManager { } // 解析磁盘文件 gif 和 svg - if(ImageKnifeData.GIF == filetype && !this.options.dontAnimateFlag){ + if (ImageKnifeData.GIF == filetype && !this.options.dontAnimateFlag) { // 处理gif - this.gifProcess(onComplete,onError,source,filetype, (imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData) - }) + this.gifProcess(onComplete, onError, source, filetype) // 保存二级磁盘缓存 Promise.resolve(source) - .then((arraybuffer: ArrayBuffer)=>{ - this.mDiskCacheProxy.putValue(this.options.generateDataKey, arraybuffer) + .then((arraybuffer: ArrayBuffer) => { + DiskLruCache.saveFileCacheOnlyFile(this.options.moduleContext?.filesDir as string, this.options.generateDataKey,arraybuffer); }) - .catch( (err:BusinessError)=>{ - LogUtil.log('download file is ='+ImageKnifeData.GIF+'and save diskLruCache error ='+ (err as BusinessError)) + .catch((err: BusinessError) => { + LogUtil.log('download file is =' + ImageKnifeData.GIF + 'and save diskLruCache error =' + (err as BusinessError)) }) - }else if(ImageKnifeData.SVG == filetype){ + } else if (ImageKnifeData.SVG == filetype) { // 处理svg - this.svgProcess(request,onComplete,onError, source, filetype, (imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData) - }) + this.svgProcess(request, onComplete, onError, source, filetype) // 保存二级磁盘缓存 Promise.resolve(source) - .then((arraybuffer: ArrayBuffer)=>{ - this.mDiskCacheProxy.putValue(this.options.generateDataKey, arraybuffer) + .then((arraybuffer: ArrayBuffer) => { + DiskLruCache.saveFileCacheOnlyFile(this.options.moduleContext?.filesDir as string, this.options.generateDataKey,arraybuffer); }) - .catch((err:BusinessError)=>{ - LogUtil.log('download file is ='+ImageKnifeData.SVG+'and save diskLruCache error ='+ (err as BusinessError)) + .catch((err: BusinessError) => { + LogUtil.log('download file is =' + ImageKnifeData.SVG + 'and save diskLruCache error =' + (err as BusinessError)) }) } else { // 进行变换 if (this.options.transformations[0]) { // thumbnail 缩略图部分 if (this.options.thumbSizeMultiplier) { - if(filetype != null) { + if (filetype != null) { this.thumbnailProcess(source, filetype, onComplete, onError); } } else { - this.options.transformations[0].transform(source, this.options, {asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { - if (pixelMap) { - if(filetype != null) { - this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); + this.options.transformations[0].transform(source, this.options, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + if (filetype != null) { + this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); + } + } else { + onError(error); } - } else { - onError(error); } - }}) + }) } } else { // thumbnail 缩略图部分 @@ -511,13 +439,12 @@ export class RequestManager { let thumbCallback = this.options.thumbholderOnComplete let thumbError = this.options.thumbholderOnError let thumbSuccess = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - thumbCallback(imageKnifeData); + thumbCallback(value); } this.mParseImageUtil.parseImageThumbnail(this.options.thumbSizeMultiplier, source, thumbSuccess, thumbError); setTimeout(() => { let success = (value: PixelMap) => { - if(filetype != null) { + if (filetype != null) { this.saveCacheAndDisk(value, filetype, onComplete, source); } } @@ -525,7 +452,7 @@ export class RequestManager { }, this.options.thumbDelayTime) } else { let success = (value: PixelMap) => { - if(filetype != null) { + if (filetype != null) { this.saveCacheAndDisk(value, filetype, onComplete, source); } } @@ -535,94 +462,90 @@ export class RequestManager { } } - createImagePixelMap(imageKnifeType: ImageKnifeType, imageKnifeValue: PixelMap): ImageKnifeData{ - return ImageKnifeData.createImagePixelMap(imageKnifeType,imageKnifeValue); + createImagePixelMap(imageKnifeType: ImageKnifeType, imageKnifeValue: PixelMap): ImageKnifeData { + return ImageKnifeData.createImagePixelMap(imageKnifeType, imageKnifeValue); } - createImageGIFFrame(imageKnifeType: ImageKnifeType, imageKnifeValue: GIFFrame[]): ImageKnifeData{ - return ImageKnifeData.createImageGIFFrame(imageKnifeType,imageKnifeValue); + createImageGIFFrame(imageKnifeType: ImageKnifeType, imageKnifeValue: GIFFrame[]): ImageKnifeData { + return ImageKnifeData.createImageGIFFrame(imageKnifeType, imageKnifeValue); } - - private saveCacheAndDisk(value: PixelMap, filetype:string, onComplete:(value:ImageKnifeData)=>void|PromiseLike, source:ArrayBuffer) { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - let save2DiskCache = (arraybuffer:ArrayBuffer) => { - this.mDiskCacheProxy.putValue(this.options.generateDataKey, arraybuffer) + private async saveCacheAndDisk(value: PixelMap, filetype: string, onComplete: (value: PixelMap) => void | PromiseLike, source: ArrayBuffer) { + let save2DiskCache = (arraybuffer: ArrayBuffer) => { + DiskLruCache.saveFileCacheOnlyFile(this.options.moduleContext?.filesDir as string, this.options.generateDataKey,arraybuffer); // 落盘之后需要主动移除当前request并且调用下一个加载 let removeCurrentAndSearchNextRun = this.options.removeCurrentAndSearchNext removeCurrentAndSearchNextRun(); } - let runSave2Disk = (resolve:(value:ArrayBuffer)=>void|PromiseLike, reject:(reason?:BusinessError|string)=>void) => { + let runSave2Disk = (resolve: (value: ArrayBuffer) => void | PromiseLike, reject: (reason?: BusinessError | string) => void) => { resolve(source); } let promise = new Promise(runSave2Disk); - promise.then(save2DiskCache); - imageKnifeData.waitSaveDisk = true; - onComplete(imageKnifeData); + await promise.then(save2DiskCache); + onComplete(value); } - thumbnailProcess(source:ArrayBuffer, filetype:string, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void){ + thumbnailProcess(source: ArrayBuffer, filetype: string, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { let thumbOption = new RequestOption(); let ctx = this.options.getModuleContext() - if(ctx != undefined){ + if (ctx != undefined) { thumbOption.setModuleContext(ctx) - }else{ + } else { onError('RequestManager thumbnailProcess moduleContext is undefined, please check it!') return } thumbOption.setImageViewSize({ width: Math.round(this.options.thumbSizeMultiplier * this.options.size.width), height: Math.round(this.options.thumbSizeMultiplier * this.options.size.height) - }) + }) let thumbCallback = this.options.thumbholderOnComplete let thumbError = this.options.thumbholderOnError - this.options.transformations[0].transform(source, thumbOption, {asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { - if (pixelMap) { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap); - thumbCallback(imageKnifeData); - } else { - thumbError(error); - } - }}) - setTimeout(() => { - this.options.transformations[0].transform(source, this.options,{asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { + this.options.transformations[0].transform(source, thumbOption, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { if (pixelMap) { - this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); + thumbCallback(pixelMap); + } else { + thumbError(error); + } + } + }) + setTimeout(() => { + this.options.transformations[0].transform(source, this.options, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); } else { onError(error); } - }}) - }, this.options.thumbDelayTime) - } - private svgProcess(option: RequestOption,onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { - let svgParseImpl = new SVGParseImpl() - svgParseImpl.parseSvg(option,arraybuffer, onComplete,onError) + } + }) + }, this.options.thumbDelayTime) } - private gifProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { + private svgProcess(option: RequestOption, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void, arraybuffer: ArrayBuffer, typeValue: string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { + let svgParseImpl = new SVGParseImpl() + svgParseImpl.parseSvg(option, arraybuffer, onComplete, onError) + } + + private gifProcess(onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void, arraybuffer: ArrayBuffer, typeValue: string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { let gifParseImpl = new GIFParseImpl() - gifParseImpl.parseGifs(arraybuffer, (data?:GIFFrame[],err?:BusinessError|string)=>{ - if(err){ + gifParseImpl.parseGifs(arraybuffer, (data?: GIFFrame[], err?: BusinessError | string) => { + if (err) { onError(err) } - LogUtil.log("gifProcess data is null:"+(data == null)); - if(!!data){ - let imageKnifeData = this.createImageGIFFrame(ImageKnifeType.GIFFRAME,data) - LogUtil.log('gifProcess 生成gif 返回数据类型') - if(cacheStrategy){ - LogUtil.log('gifProcess 生成gif并且存入了缓存策略') - cacheStrategy(imageKnifeData) - } - onComplete(imageKnifeData) - }else{ + LogUtil.log("gifProcess data is null:" + (data == null)); + if (!!data) { + // let imageKnifeData = this.createImageGIFFrame(ImageKnifeType.GIFFRAME, data) + // LogUtil.log('gifProcess 生成gif 返回数据类型') + // if (cacheStrategy) { + // LogUtil.log('gifProcess 生成gif并且存入了缓存策略') + // cacheStrategy(imageKnifeData) + // } + onComplete(data) + } else { onError('Parse GIF callback data is null, you need check callback data!') } }) } - - } - - diff --git a/library/src/main/ets/components/imageknife/transform/BaseTransform.ets b/library/src/main/ets/components/imageknife/transform/BaseTransform.ets index 35847ab..e0a7f1c 100644 --- a/library/src/main/ets/components/imageknife/transform/BaseTransform.ets +++ b/library/src/main/ets/components/imageknife/transform/BaseTransform.ets @@ -20,5 +20,9 @@ export interface BaseTransform { // 实现类 返回作为key生成的一部分 getName(): string; + getClassName():string; + + getConstructorParams():string; + transform(value: ArrayBuffer, request: RequestOption, func: AsyncTransform):void; } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/transform/BlurTransformation.ets b/library/src/main/ets/components/imageknife/transform/BlurTransformation.ets index 2c30581..3cd6547 100644 --- a/library/src/main/ets/components/imageknife/transform/BlurTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/BlurTransformation.ets @@ -37,6 +37,14 @@ export class BlurTransformation implements BaseTransform { } } + getClassName() { + return "BlurTransformation"; + } + + getConstructorParams() { + return JSON.stringify([this._mRadius,this.sampling]); + } + getName() { return "BlurTransformation _mRadius:" + this._mRadius +"==BlurTransformation sampling:"+ this.sampling; } diff --git a/library/src/main/ets/components/imageknife/transform/BrightnessFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/BrightnessFilterTransformation.ets index a63feb8..cd9d884 100644 --- a/library/src/main/ets/components/imageknife/transform/BrightnessFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/BrightnessFilterTransformation.ets @@ -33,6 +33,14 @@ export class BrightnessFilterTransformation implements BaseTransform { this._mBrightness = brightness; } + getClassName(){ + return "BrightnessFilterTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this._mBrightness]); + } + getName() { return "BrightnessFilterTransformation:" + this._mBrightness; } diff --git a/library/src/main/ets/components/imageknife/transform/ContrastFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/ContrastFilterTransformation.ets index 7aa272b..6454511 100644 --- a/library/src/main/ets/components/imageknife/transform/ContrastFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/ContrastFilterTransformation.ets @@ -45,6 +45,15 @@ export class ContrastFilterTransformation implements BaseTransform { this._mContrast = contrast; } + getClassName(){ + return "ContrastFilterTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this._mContrast]); + } + + getName() { return "ContrastFilterTransformation:" + this._mContrast; } diff --git a/library/src/main/ets/components/imageknife/transform/CropCircleTransformation.ets b/library/src/main/ets/components/imageknife/transform/CropCircleTransformation.ets index a3aba0c..14a2d02 100644 --- a/library/src/main/ets/components/imageknife/transform/CropCircleTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/CropCircleTransformation.ets @@ -29,6 +29,14 @@ export class CropCircleTransformation implements BaseTransform { private mCenterY: number = 0; private mRadius: number = 0; + getClassName(){ + return "CropCircleTransformation"; + } + + getConstructorParams(){ + return "[]"; + } + getName() { return CropCircleTransformation.TAG + ";mCenterX:" + this.mCenterX + ";mCenterY:" + this.mCenterY diff --git a/library/src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation.ets b/library/src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation.ets index ea3cc38..582cb40 100644 --- a/library/src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation.ets @@ -18,15 +18,17 @@ import { AsyncTransform } from "../transform/AsyncTransform" import { Constants } from "../constants/Constants" import { RequestOption } from "../../imageknife/RequestOption" import { TransformUtils } from "../transform/TransformUtils" -import {LogUtil} from '../../imageknife/utils/LogUtil' +import { LogUtil } from '../../imageknife/utils/LogUtil' import image from "@ohos.multimedia.image" import { BusinessError } from '@ohos.base' -import {Size} from '../../imageknife/RequestOption' -export interface rgbColor{ +import { Size } from '../../imageknife/RequestOption' + +export interface rgbColor { r_color: number, g_color: number, b_color: number, } + export class CropCircleWithBorderTransformation implements BaseTransform { private static TAG: string = "CropCircleTransformation"; private mBorderSize: number = 5; @@ -35,7 +37,7 @@ export class CropCircleWithBorderTransformation implements BaseTransform) { @@ -62,28 +76,30 @@ export class CropCircleWithBorderTransformation implements BaseTransform { - if (!size) { - func?.asyncTransform(error, null) - return; + let imageSource: image.ImageSource = image.createImageSource(buf); + TransformUtils.getPixelMapSize(imageSource, { + asyncTransform: (error: BusinessError | string, size: Size | null) => { + if (!size) { + func?.asyncTransform(error, null) + return; + } + let pixelMapWidth = size.width; + let pixelMapHeight = size.height; + let targetWidth = request.size.width; + let targetHeight = request.size.height; + if (pixelMapWidth < targetWidth) { + targetWidth = pixelMapWidth; + } + if (pixelMapHeight < targetHeight) { + targetHeight = pixelMapHeight; + } + this.updatePixelMapSize(imageSource, targetWidth, targetHeight, func); } - let pixelMapWidth = size.width; - let pixelMapHeight = size.height; - let targetWidth = request.size.width; - let targetHeight = request.size.height; - if (pixelMapWidth < targetWidth) { - targetWidth = pixelMapWidth; - } - if (pixelMapHeight < targetHeight) { - targetHeight = pixelMapHeight; - } - this.updatePixelMapSize(imageSource, targetWidth, targetHeight, func); - }}) + }) } private updatePixelMapSize(imageSource: image.ImageSource, outWith: number, outHeight: number, func?: AsyncTransform) { - let options:image.DecodingOptions = { + let options: image.DecodingOptions = { editable: true, desiredSize: { width: outWith, @@ -91,13 +107,13 @@ export class CropCircleWithBorderTransformation implements BaseTransform { + .then((pixelMap: PixelMap) => { this.transformPixelMap(pixelMap, outWith, outHeight, func); imageSource.release() }) - .catch((e:BusinessError) => { + .catch((e: BusinessError) => { LogUtil.log(Constants.PROJECT_TAG + ";CropCircleWithBorderTransformation e:" + e); - if (func!=undefined) { + if (func != undefined) { func?.asyncTransform(Constants.PROJECT_TAG + ";CropCircleWithBorderTransformation e:" + e, null); } imageSource.release() @@ -120,8 +136,8 @@ export class CropCircleWithBorderTransformation implements BaseTransform { private static TAG: string = "CropSquareTransformation"; + getClassName(){ + return "CropSquareTransformation"; + } + + getConstructorParams(){ + return '[]'; + } + getName() { return CropSquareTransformation.TAG; } diff --git a/library/src/main/ets/components/imageknife/transform/CropTransformation.ets b/library/src/main/ets/components/imageknife/transform/CropTransformation.ets index 416af67..9cf40b2 100644 --- a/library/src/main/ets/components/imageknife/transform/CropTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/CropTransformation.ets @@ -35,6 +35,14 @@ export class CropTransformation implements BaseTransform { this.mCropType = cropType; } + getClassName(){ + return "CropTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this.mWidth,this.mHeight ,this.mCropType]); + } + getName() { return CropTransformation.TAG + ";mWidth:" + this.mWidth + ";mHeight:" + this.mHeight diff --git a/library/src/main/ets/components/imageknife/transform/GrayscaleTransformation.ets b/library/src/main/ets/components/imageknife/transform/GrayscaleTransformation.ets index 9a07816..bc6771d 100644 --- a/library/src/main/ets/components/imageknife/transform/GrayscaleTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/GrayscaleTransformation.ets @@ -29,6 +29,14 @@ export class GrayscaleTransformation implements BaseTransform { return "GrayscaleTransformation"; } + getClassName(){ + return "GrayscaleTransformation"; + } + + getConstructorParams(){ + return '[]'; + } + async transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { LogUtil.log(Constants.PROJECT_TAG + ";GrayscaleTransformation buf is empty"); diff --git a/library/src/main/ets/components/imageknife/transform/InvertFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/InvertFilterTransformation.ets index c8a3e8c..7373ae8 100644 --- a/library/src/main/ets/components/imageknife/transform/InvertFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/InvertFilterTransformation.ets @@ -36,6 +36,14 @@ export class InvertFilterTransformation implements BaseTransform { return "InvertFilterTransformation"; } + getClassName(){ + return "InvertFilterTransformation"; + } + + getConstructorParams(){ + return '[]'; + } + async transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { LogUtil.log(Constants.PROJECT_TAG + ";InvertFilterTransformation buf is empty"); diff --git a/library/src/main/ets/components/imageknife/transform/KuwaharaFilterTransform.ets b/library/src/main/ets/components/imageknife/transform/KuwaharaFilterTransform.ets index 2ecefa9..9b87c41 100644 --- a/library/src/main/ets/components/imageknife/transform/KuwaharaFilterTransform.ets +++ b/library/src/main/ets/components/imageknife/transform/KuwaharaFilterTransform.ets @@ -32,6 +32,14 @@ export class KuwaharaFilterTransform implements BaseTransform { this._mRadius = radius; } + getClassName(){ + return "KuwaharaFilterTransform"; + } + + getConstructorParams(){ + return JSON.stringify([this._mRadius]); + } + getName() { return "KuwaharaFilterTransform _mRadius:" + this._mRadius; } diff --git a/library/src/main/ets/components/imageknife/transform/MaskTransformation.ets b/library/src/main/ets/components/imageknife/transform/MaskTransformation.ets index ed7d7d0..8a73ce7 100644 --- a/library/src/main/ets/components/imageknife/transform/MaskTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/MaskTransformation.ets @@ -35,6 +35,14 @@ export class MaskTransformation implements BaseTransform { this._mResourceData = maskBitmap; } + getClassName(){ + return "MaskTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this._mResourceData]); + } + getName() { return "MaskTransformation:" + this._mResourceData; } diff --git a/library/src/main/ets/components/imageknife/transform/PixelationFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/PixelationFilterTransformation.ets index ff67eba..341b42a 100644 --- a/library/src/main/ets/components/imageknife/transform/PixelationFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/PixelationFilterTransformation.ets @@ -37,6 +37,14 @@ export class PixelationFilterTransformation implements BaseTransform { } } + getClassName(){ + return "PixelationFilterTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this._mPixel]); + } + getName() { return "PixelationFilterTransformation" + this._mPixel; } diff --git a/library/src/main/ets/components/imageknife/transform/RotateImageTransformation.ets b/library/src/main/ets/components/imageknife/transform/RotateImageTransformation.ets index 6286720..3cc9656 100644 --- a/library/src/main/ets/components/imageknife/transform/RotateImageTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/RotateImageTransformation.ets @@ -34,6 +34,14 @@ export class RotateImageTransformation implements BaseTransform { return "RotateImageTransformation" + ";degreesToRotate:" + this.mDegreesToRotate; } + getClassName(){ + return "RotateImageTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this.mDegreesToRotate]); + } + transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { LogUtil.log(Constants.PROJECT_TAG + ";RotateImageTransformation buf is empty"); diff --git a/library/src/main/ets/components/imageknife/transform/RoundedCornersTransformation.ets b/library/src/main/ets/components/imageknife/transform/RoundedCornersTransformation.ets index a620470..2fdebef 100644 --- a/library/src/main/ets/components/imageknife/transform/RoundedCornersTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/RoundedCornersTransformation.ets @@ -46,6 +46,19 @@ export class RoundedCornersTransformation implements BaseTransform { this.mBottom_right = value.bottom_right; } + getClassName(){ + return "RoundedCornersTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([{ + top_left: this.mTop_left, + top_right: this.mTop_right, + bottom_left: this.mBottom_left, + bottom_right: this.mBottom_right + }]); + } + getName() { return "RoundedCornersTransformation" + ";mTop_left:" + this.mTop_left + ";mTop_right:" + this.mTop_right diff --git a/library/src/main/ets/components/imageknife/transform/SepiaFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/SepiaFilterTransformation.ets index 87380d0..ab4e99f 100644 --- a/library/src/main/ets/components/imageknife/transform/SepiaFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/SepiaFilterTransformation.ets @@ -33,6 +33,14 @@ export class SepiaFilterTransformation implements BaseTransform { return "SepiaFilterTransformation"; } + getClassName(){ + return "SepiaFilterTransformation"; + } + + getConstructorParams(){ + return '[]'; + } + async transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { LogUtil.log(Constants.PROJECT_TAG + ";SepiaFilterTransformation buf is empty"); diff --git a/library/src/main/ets/components/imageknife/transform/SketchFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/SketchFilterTransformation.ets index 25d6f6c..1092912 100644 --- a/library/src/main/ets/components/imageknife/transform/SketchFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/SketchFilterTransformation.ets @@ -24,6 +24,15 @@ import { BusinessError } from '@ohos.base' import {Size} from '../../imageknife/RequestOption' export class SketchFilterTransformation implements BaseTransform { + + getClassName(){ + return "SketchFilterTransformation"; + } + + getConstructorParams(){ + return '[]'; + } + getName() { return 'SketchFilterTransformation'; } diff --git a/library/src/main/ets/components/imageknife/transform/SwirlFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/SwirlFilterTransformation.ets index 5681f1e..76c5a14 100644 --- a/library/src/main/ets/components/imageknife/transform/SwirlFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/SwirlFilterTransformation.ets @@ -43,6 +43,14 @@ export class SwirlFilterTransformation implements BaseTransform { } } + getClassName(){ + return "SwirlFilterTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this.radius,this._angle,[this._xCenter,this._yCenter]]); + } + getName() { return 'SwirlFilterTransformation' + this.radius; } diff --git a/library/src/main/ets/components/imageknife/transform/ToonFilterTransform.ets b/library/src/main/ets/components/imageknife/transform/ToonFilterTransform.ets index cb75d92..3b5dcde 100644 --- a/library/src/main/ets/components/imageknife/transform/ToonFilterTransform.ets +++ b/library/src/main/ets/components/imageknife/transform/ToonFilterTransform.ets @@ -37,6 +37,14 @@ export class ToonFilterTransform implements BaseTransform { } } + getClassName(){ + return "ToonFilterTransform"; + } + + getConstructorParams(){ + return JSON.stringify([this.threshold,this.quantizationLevels]); + } + getName() { return "ToonFilterTransform threshold:" + this.threshold + ";quantizationLevels:" + this.quantizationLevels; } diff --git a/library/src/main/ets/components/imageknife/transform/VignetteFilterTransform.ets b/library/src/main/ets/components/imageknife/transform/VignetteFilterTransform.ets index d1937ce..c612c4d 100644 --- a/library/src/main/ets/components/imageknife/transform/VignetteFilterTransform.ets +++ b/library/src/main/ets/components/imageknife/transform/VignetteFilterTransform.ets @@ -42,6 +42,14 @@ export class VignetteFilterTransform implements BaseTransform { } } + getClassName(){ + return "VignetteFilterTransform"; + } + + getConstructorParams(){ + return JSON.stringify([this.centerPoint,this.vignetteColor,this.vignetteSpace]); + } + getName() { return "VignetteFilterTransform centerPoint:" + this.centerPoint + ";vignetteColor:" + this.vignetteColor + ";vignetteSpace:" + this.vignetteSpace; } diff --git a/library/src/main/ets/components/imageknife/transform/pixelmap/CenterCrop.ets b/library/src/main/ets/components/imageknife/transform/pixelmap/CenterCrop.ets index 01b8b5d..0c23002 100644 --- a/library/src/main/ets/components/imageknife/transform/pixelmap/CenterCrop.ets +++ b/library/src/main/ets/components/imageknife/transform/pixelmap/CenterCrop.ets @@ -20,6 +20,15 @@ import {TransformUtils} from '../TransformUtils' import {RequestOption} from '../../../imageknife/RequestOption' import { BusinessError } from '@ohos.base' export class CenterCrop implements BaseTransform { + + getClassName(){ + return "CenterCrop"; + } + + getConstructorParams(){ + return '[]'; + } + getName() { return 'CenterCrop:' + this; } diff --git a/library/src/main/ets/components/imageknife/transform/pixelmap/CenterInside.ets b/library/src/main/ets/components/imageknife/transform/pixelmap/CenterInside.ets index 94cb818..68264ae 100644 --- a/library/src/main/ets/components/imageknife/transform/pixelmap/CenterInside.ets +++ b/library/src/main/ets/components/imageknife/transform/pixelmap/CenterInside.ets @@ -23,6 +23,13 @@ export class CenterInside implements BaseTransform { getName() { return 'CenterInside:' + this; } + getClassName(){ + return "CenterInside"; + } + + getConstructorParams(){ + return '[]'; + } transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { diff --git a/library/src/main/ets/components/imageknife/transform/pixelmap/FitCenter.ets b/library/src/main/ets/components/imageknife/transform/pixelmap/FitCenter.ets index 324d164..907d828 100644 --- a/library/src/main/ets/components/imageknife/transform/pixelmap/FitCenter.ets +++ b/library/src/main/ets/components/imageknife/transform/pixelmap/FitCenter.ets @@ -23,6 +23,13 @@ export class FitCenter implements BaseTransform { getName() { return 'FitCenter:' + this; } + getClassName(){ + return "FitCenter"; + } + + getConstructorParams(){ + return '[]'; + } transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { diff --git a/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets b/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets index 6362253..9b94445 100644 --- a/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets +++ b/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets @@ -27,33 +27,33 @@ export class ParseImageUtil implements IParseImage { // scale(0,1) parseImageThumbnail(scale: number, imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void) { - // let imageSource: image.ImageSource = image.createImageSource(imageinfo); // 步骤一:文件转为pixelMap 然后变换 给Image组件 - // imageSource.getImageInfo().then((value) => { - // let hValue = Math.round(value.size.height * scale); - // let wValue = Math.round(value.size.width * scale); - // let defaultSize: image.Size = { - // height: hValue, - // width: wValue - // }; - // let opts: image.DecodingOptions = { - // editable: true, - // desiredSize: defaultSize - // }; - // - // imageSource.createPixelMap(opts).then((pixelMap: image.PixelMap) => { - // onCompleteFunction(pixelMap); - // imageSource.release() - // }).catch((err: string) => { - // onErrorFunction(err); - // imageSource.release() - // }) - // - // }).catch((err: string) => { - // onErrorFunction(err); - // imageSource.release() - // }) + let imageSource: image.ImageSource = image.createImageSource(imageinfo); // 步骤一:文件转为pixelMap 然后变换 给Image组件 + imageSource.getImageInfo().then((value) => { + let hValue = Math.round(value.size.height * scale); + let wValue = Math.round(value.size.width * scale); + let defaultSize: image.Size = { + height: hValue, + width: wValue + }; + let opts: image.DecodingOptions = { + editable: true, + desiredSize: defaultSize + }; - taskPoolExecutePixelMap(imageinfo,scale,onCompleteFunction,onErrorFunction); //多线程接口 + imageSource.createPixelMap(opts).then((pixelMap: image.PixelMap) => { + onCompleteFunction(pixelMap); + imageSource.release() + }).catch((err: string) => { + onErrorFunction(err); + imageSource.release() + }) + + }).catch((err: string) => { + onErrorFunction(err); + imageSource.release() + }) + + // taskPoolExecutePixelMap(imageinfo,scale,onCompleteFunction,onErrorFunction); //多线程接口 } } @@ -79,14 +79,14 @@ async function taskParseImage(arrayBuffer: ArrayBuffer, scale: number): Promise< } function taskPoolExecutePixelMap(arrayBuffer: ArrayBuffer, scale: number, onCompleteFunction: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void) { - LogUtil.log("ceshi321 : arrayBuffer长度" + arrayBuffer.byteLength) - let task = new taskpool.Task(taskParseImage, arrayBuffer, scale) - task.setTransferList([]) + LogUtil.log("ceshi321 : arrayBuffer长度" + arrayBuffer.byteLength); + let task = new taskpool.Task(taskParseImage, arrayBuffer, scale); + task.setTransferList([]); taskpool.execute(task).then((pixelmap: Object) => { LogUtil.log('ceshi321 : Succeeded in creating pixelmap Ui .' + (pixelmap as image.PixelMap).getPixelBytesNumber()) onCompleteFunction(pixelmap as image.PixelMap); }).catch((err: string) => { - LogUtil.log("ceshi321 : test occur error: " + err) + LogUtil.log("ceshi321 : test occur error: " + err); onErrorFunction(err); }); } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets b/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets index 772aadd..0d63892 100644 --- a/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets +++ b/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets @@ -36,46 +36,46 @@ export interface gifBackData { export class GIFParseImpl implements IParseGif { parseGifs(imageinfo: ArrayBuffer, callback: (data?: GIFFrame[], err?: BusinessError | string) => void) { // 硬解码流程 - // let imageSource = image.createImageSource(imageinfo); - // let decodeOpts: image.DecodingOptions = { - // sampleSize: 1, - // editable: true, - // rotate: 0 - // } - // let data:GIFFrame[] = []; - // imageSource.createPixelMapList(decodeOpts).then((pixelList: Array) => { - // //sdk的api接口发生变更:从.getDelayTime() 变为.getDelayTimeList() - // imageSource.getDelayTimeList().then(delayTimes => { - // if (pixelList.length > 0) { - // let pixelmap1 = pixelList[0]; - // pixelmap1.getImageInfo().then(imageInfo => { - // for (let i = 0; i < pixelList.length; i++) { - // let frame = new GIFFrame(); - // frame.drawPixelMap = pixelList[i]; - // frame.dims = { width: imageInfo.size.width, height: imageInfo.size.height, top: 0, left: 0 } - // if (i < delayTimes.length) { - // frame.delay = delayTimes[i]; - // } else { - // frame.delay = delayTimes[delayTimes.length - 1] - // } - // data.push(frame) - // } - // callback(data,undefined) - // imageSource.release(); - // }).catch((err: string) => { - // imageSource.release(); - // callback(undefined,err) - // }) - // } - // }).catch((err: string) => { - // imageSource.release(); - // callback(undefined,err) - // }) - // }).catch((err: string) => { - // imageSource.release(); - // callback(undefined,err) - // }) - taskPoolExecutePixelMapList(imageinfo,callback); //多线程接口 + let imageSource = image.createImageSource(imageinfo); + let decodeOpts: image.DecodingOptions = { + sampleSize: 1, + editable: true, + rotate: 0 + } + let data:GIFFrame[] = []; + imageSource.createPixelMapList(decodeOpts).then((pixelList: Array) => { + //sdk的api接口发生变更:从.getDelayTime() 变为.getDelayTimeList() + imageSource.getDelayTimeList().then(delayTimes => { + if (pixelList.length > 0) { + let pixelmap1 = pixelList[0]; + pixelmap1.getImageInfo().then(imageInfo => { + for (let i = 0; i < pixelList.length; i++) { + let frame = new GIFFrame(); + frame.drawPixelMap = pixelList[i]; + frame.dims = { width: imageInfo.size.width, height: imageInfo.size.height, top: 0, left: 0 } + if (i < delayTimes.length) { + frame.delay = delayTimes[i]; + } else { + frame.delay = delayTimes[delayTimes.length - 1] + } + data.push(frame) + } + callback(data,undefined) + imageSource.release(); + }).catch((err: string) => { + imageSource.release(); + callback(undefined,err) + }) + } + }).catch((err: string) => { + imageSource.release(); + callback(undefined,err) + }) + }).catch((err: string) => { + imageSource.release(); + callback(undefined,err) + }) + //taskPoolExecutePixelMapList(imageinfo,callback); //多线程接口 } } diff --git a/library/src/main/ets/components/imageknife/utils/svg/IParseSvg.ets b/library/src/main/ets/components/imageknife/utils/svg/IParseSvg.ets index 2863f30..bf7c72e 100644 --- a/library/src/main/ets/components/imageknife/utils/svg/IParseSvg.ets +++ b/library/src/main/ets/components/imageknife/utils/svg/IParseSvg.ets @@ -19,6 +19,6 @@ import { ImageKnifeData } from '../../ImageKnifeData' export interface IParseSvg { // 解析svg parseSvg:(option: RequestOption, imageInfo: ArrayBuffer | ArrayBufferLike, - onComplete: (value: ImageKnifeData) => void | PromiseLike, + onComplete: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void)=> void } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets b/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets index ceb80d8..557214a 100644 --- a/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets +++ b/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets @@ -20,7 +20,7 @@ import image from '@ohos.multimedia.image' export class SVGParseImpl implements IParseSvg { parseSvg(option: RequestOption, imageInfo: ArrayBuffer, - onComplete: (value: ImageKnifeData) => void | PromiseLike, + onComplete: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void) { let imageSource: image.ImageSource = image.createImageSource(imageInfo); // 步骤一:文件转为pixelMap 然后变换 给Image组件 @@ -36,7 +36,7 @@ export class SVGParseImpl implements IParseSvg { }; imageSource.createPixelMap(opts).then((pixelMap: image.PixelMap) => { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap) - onComplete(imageKnifeData); + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap); imageSource.release() }).catch((err: string) => { onErrorFunction(err);