1.新增MemoryLruCache主动调用PixelMap的release方法,释放native的PixelMap内存

2.新增ImageSource主动调用release方法释放native持有的ImageSource内存

Signed-off-by: zhoulisheng <635547767@qq.com>
This commit is contained in:
zhoulisheng 2023-11-02 10:07:39 +08:00
parent fcec94020d
commit 3077af9dc3
49 changed files with 913 additions and 380 deletions

View File

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

View File

@ -1,6 +1,8 @@
## 2.1.1-rc.1 ## 2.1.1-rc.1
- 新增自定义key参数配置 - 新增自定义key参数配置
- 新增MemoryLruCache主动调用PixelMap的release方法,释放native的PixelMap内存
- 新增ImageSource主动调用release方法释放native持有的ImageSource内存
## 2.1.1-rc.0 ## 2.1.1-rc.0

View File

@ -37,7 +37,7 @@ ohpm install @ohos/imageknife
"author": "", "author": "",
"license": "", "license": "",
"dependencies": { "dependencies": {
"@ohos/imageknife": "^2.0.6" "@ohos/imageknife": "^2.1.1-rc.1"
} }
} }
``` ```

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,63 +14,71 @@
*/ */
import { DiskLruCache } from "@ohos/disklrucache" import { DiskLruCache } from "@ohos/disklrucache"
import { LruCache } from "../cache/LruCache" import { EngineKeyFactories } from "../cache/key/EngineKeyFactories"
import {EngineKeyFactories} from "../cache/key/EngineKeyFactories" import { EngineKeyInterface } from "../cache/key/EngineKeyInterface"
import {EngineKeyInterface} from "../cache/key/EngineKeyInterface" import { RequestOption } from "../imageknife/RequestOption"
import {RequestOption} from "../imageknife/RequestOption" import { AsyncCallback } from "../imageknife/interface/AsyncCallback"
import {AsyncCallback} from "../imageknife/interface/AsyncCallback" import { PlaceHolderManager } from "../imageknife/holder/PlaceHolderManager"
import {PlaceHolderManager} from "../imageknife/holder/PlaceHolderManager" import { RetryHolderManager } from "../imageknife/holder/RetryHolderManager"
import {RetryHolderManager} from "../imageknife/holder/RetryHolderManager" import { ErrorHolderManager } from "../imageknife/holder/ErrorHolderManager"
import {ErrorHolderManager} from "../imageknife/holder/ErrorHolderManager" import { RequestManager } from "../imageknife/requestmanage/RequestManager"
import {RequestManager} from "../imageknife/requestmanage/RequestManager" import { NONE } from "../cache/diskstrategy/enum/NONE"
import {NONE} from "../cache/diskstrategy/enum/NONE" import { FileTypeUtil } from '../imageknife/utils/FileTypeUtil'
import {FileTypeUtil} from '../imageknife/utils/FileTypeUtil' import { DownloadClient } from '../imageknife/networkmanage/DownloadClient'
import {DownloadClient} from '../imageknife/networkmanage/DownloadClient' import { IDataFetch } from '../imageknife/networkmanage/IDataFetch'
import {IDataFetch} from '../imageknife/networkmanage/IDataFetch' import { ParseResClient } from '../imageknife/resourcemanage/ParseResClient'
import {ParseResClient} from '../imageknife/resourcemanage/ParseResClient' import { IResourceFetch } from '../imageknife/resourcemanage/IResourceFetch'
import {IResourceFetch} from '../imageknife/resourcemanage/IResourceFetch' import { ImageKnifeData, ImageKnifeType } from '../imageknife/ImageKnifeData'
import {ImageKnifeData,ImageKnifeType} from '../imageknife/ImageKnifeData' import { ImageKnifeGlobal } from '../imageknife/ImageKnifeGlobal'
import {ImageKnifeGlobal} from '../imageknife/ImageKnifeGlobal'
import image from "@ohos.multimedia.image" import image from "@ohos.multimedia.image"
import {CompressBuilder} from "../imageknife/compress/CompressBuilder" import { CompressBuilder } from "../imageknife/compress/CompressBuilder"
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle' import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
import {LogUtil} from '../imageknife/utils/LogUtil' import { LogUtil } from '../imageknife/utils/LogUtil'
import { EasyLinkedHashMap } from './utils/base/EasyLinkedHashMap'
import { MethodMutex } from './utils/base/MethodMutex'
import worker from '@ohos.worker' import worker from '@ohos.worker'
import common from '@ohos.app.ability.common' import common from '@ohos.app.ability.common'
import HashMap from '@ohos.util.HashMap'
import LinkedList from '@ohos.util.LinkedList'
import { MemoryLruCache } from '../cache/MemoryLruCache'
export class ImageKnife { export class ImageKnife {
static readonly SEPARATOR: string = '/' static readonly SEPARATOR: string = '/'
memoryCache: MemoryLruCache;
memoryCache: LruCache<string, ImageKnifeData>; diskMemoryCache: DiskLruCache;
diskMemoryCache: DiskLruCache; dataFetch: IDataFetch;
dataFetch: IDataFetch; resourceFetch: IResourceFetch<ArrayBuffer>;
resourceFetch: IResourceFetch<ArrayBuffer>; filesPath: string = ""; // data/data/包名/files目录
filesPath: string = ""; // data/data/包名/files目录
placeholderCache: string = "placeholderCache" placeholderCache: string = "placeholderCache"
runningRequest: Array<RequestOption>; runningMaps: EasyLinkedHashMap<string, RequestOption>;
pendingRequest: Array<RequestOption>; pendingMaps: EasyLinkedHashMap<string, RequestOption>;
fileTypeUtil: FileTypeUtil; // 通用文件格式辨别 pausedMaps: EasyLinkedHashMap<string, RequestOption>;
diskCacheFolder: string = "ImageKnifeDiskCache" isPaused: boolean = false;
mutex: MethodMutex = new MethodMutex();
fileTypeUtil: FileTypeUtil; // 通用文件格式辨别
defaultListener: AsyncCallback<ImageKnifeData> = { diskCacheFolder: string = "ImageKnifeDiskCache"
callback:(err: string, data: ImageKnifeData)=>{return false} defaultListener: AsyncCallback<ImageKnifeData> = {
callback: (err: string, data: ImageKnifeData) => {
return false
}
}; // 全局监听器 }; // 全局监听器
// gifWorker // gifWorker
gifWorker: worker.ThreadWorker|undefined = undefined; gifWorker: worker.ThreadWorker | undefined = undefined;
defaultLifeCycle: IDrawLifeCycle | undefined = undefined;
defaultLifeCycle: IDrawLifeCycle|undefined = undefined;
// 开发者可配置全局缓存 // 开发者可配置全局缓存
engineKeyImpl: EngineKeyInterface; engineKeyImpl: EngineKeyInterface;
private constructor() { private constructor() {
this.runningMaps = new EasyLinkedHashMap();
this.pendingMaps = new EasyLinkedHashMap();
this.pausedMaps = new EasyLinkedHashMap();
// 构造方法传入size 为保存文件个数 // 构造方法传入size 为保存文件个数
this.memoryCache = new LruCache<string, ImageKnifeData>(100); this.memoryCache = new MemoryLruCache(100);
// 创建disk缓存 传入的size 为多少比特 比如20KB 传入20*1024 // 创建disk缓存 传入的size 为多少比特 比如20KB 传入20*1024
this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext()); this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext());
@ -84,22 +92,21 @@ export class ImageKnife {
// 初始化本地 文件保存 // 初始化本地 文件保存
this.filesPath = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).filesDir as string; this.filesPath = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).filesDir as string;
this.runningRequest = new Array();
this.pendingRequest = new Array();
// 通用文件格式识别初始化 // 通用文件格式识别初始化
this.fileTypeUtil = new FileTypeUtil(); this.fileTypeUtil = new FileTypeUtil();
this.engineKeyImpl = new EngineKeyFactories(); this.engineKeyImpl = new EngineKeyFactories();
} }
getMemoryCache(): LruCache<string, ImageKnifeData>{ getMemoryCache(): MemoryLruCache {
return this.memoryCache; return this.memoryCache;
} }
public static with(context:Object): ImageKnifeGlobal{ public static with(context: Object): ImageKnifeGlobal {
// 存入hapContext; // 存入hapContext;
let global:ImageKnifeGlobal = ImageKnifeGlobal.getInstance(); let global: ImageKnifeGlobal = ImageKnifeGlobal.getInstance();
global.setHapContext(context) global.setHapContext(context)
// 初始化ImageKnife // 初始化ImageKnife
@ -113,7 +120,7 @@ export class ImageKnife {
return global; return global;
} }
getDiskMemoryCache(): DiskLruCache{ getDiskMemoryCache(): DiskLruCache {
return this.diskMemoryCache; return this.diskMemoryCache;
}; };
@ -121,7 +128,7 @@ export class ImageKnife {
this.diskMemoryCache = diskLruCache; this.diskMemoryCache = diskLruCache;
}; };
getFileTypeUtil(): FileTypeUtil{ getFileTypeUtil(): FileTypeUtil {
return this.fileTypeUtil; return this.fileTypeUtil;
} }
@ -129,7 +136,7 @@ export class ImageKnife {
return ImageKnifeGlobal.getInstance().getHapContext(); return ImageKnifeGlobal.getInstance().getHapContext();
} }
setMemoryCache(lrucache: LruCache<string, ImageKnifeData>) { setMemoryCache(lrucache: MemoryLruCache) {
this.memoryCache = lrucache; this.memoryCache = lrucache;
} }
@ -137,22 +144,23 @@ export class ImageKnife {
return this.defaultListener; return this.defaultListener;
} }
setGifWorker(worker:worker.ThreadWorker){ setGifWorker(worker: worker.ThreadWorker) {
this.gifWorker = worker this.gifWorker = worker
} }
getGifWorker(){
getGifWorker() {
return this.gifWorker; return this.gifWorker;
} }
getDefaultLifeCycle(){ getDefaultLifeCycle() {
return this.defaultLifeCycle; return this.defaultLifeCycle;
} }
setDefaultLifeCycle(viewLifeCycle:IDrawLifeCycle){ setDefaultLifeCycle(viewLifeCycle: IDrawLifeCycle) {
this.defaultLifeCycle = viewLifeCycle; this.defaultLifeCycle = viewLifeCycle;
} }
setEngineKeyImpl(impl:EngineKeyInterface){ setEngineKeyImpl(impl: EngineKeyInterface) {
this.engineKeyImpl = impl; this.engineKeyImpl = impl;
} }
@ -162,34 +170,34 @@ export class ImageKnife {
this.defaultListener = newDefaultListener; this.defaultListener = newDefaultListener;
} }
public compressBuilder(): CompressBuilder{ public compressBuilder(): CompressBuilder {
return new CompressBuilder(); return new CompressBuilder();
} }
// 替代原来的LruCache // 替代原来的LruCache
public replaceLruCache(size:number){ public replaceLruCache(size: number) {
if(this.memoryCache.map.size() <= 0) { if (this.memoryCache.map.size() <= 0) {
this.memoryCache = new LruCache<string, ImageKnifeData>(size); this.memoryCache = new MemoryLruCache(size);
}else{ } else {
let newLruCache = new LruCache<string, ImageKnifeData>(size); let newLruCache = new MemoryLruCache(size);
this.memoryCache.foreachLruCache( (value:ImageKnifeData, key:string, map:Object)=> { this.memoryCache.foreachLruCache((value: ImageKnifeData, key: string, map: Object) => {
newLruCache.put(key, value); newLruCache.put(key, value);
}) })
this.memoryCache = newLruCache; this.memoryCache = newLruCache;
} }
} }
public replaceDataFetch(fetch:IDataFetch){ public replaceDataFetch(fetch: IDataFetch) {
this.dataFetch = fetch; this.dataFetch = fetch;
} }
// 替代原来的DiskLruCache // 替代原来的DiskLruCache
public replaceDiskLruCache(size:number) { public replaceDiskLruCache(size: number) {
if (this.diskMemoryCache.getCacheMap().size() <= 0) { if (this.diskMemoryCache.getCacheMap().size() <= 0) {
this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size); this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size);
} else { } else {
let newDiskLruCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size); let newDiskLruCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size);
this.diskMemoryCache.foreachDiskLruCache( (value:string|ArrayBuffer, key:string, map:Object)=> { this.diskMemoryCache.foreachDiskLruCache((value: string | ArrayBuffer, key: string, map: Object) => {
newDiskLruCache.set(key, value); newDiskLruCache.set(key, value);
}) })
this.diskMemoryCache = newDiskLruCache; this.diskMemoryCache = newDiskLruCache;
@ -197,17 +205,74 @@ export class ImageKnife {
} }
// 预加载 resource资源一级缓存string资源实现二级缓存 // 预加载 resource资源一级缓存string资源实现二级缓存
preload(request: RequestOption):void { preload(request: RequestOption): void {
// 每个request 公共信息补充 // 每个request 公共信息补充
request.setFilesPath(this.filesPath); request.setFilesPath(this.filesPath);
return this.parseSource(request); return this.parseSource(request);
} }
// // 暂停所有请求
// async pauseRequests(): Promise<void> {
// await this.mutex.lock(async () => {
// this.isPaused = true;
//
// // 将未删除的所有request [run pend] 放入 [pause]
// this.pausedMaps.clear()
// console.log('dodo pauseRequests start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
//
// // 将run存入pause
// let runNode = this.runningMaps.getHead();
// while (runNode) {
// let request = runNode.value;
// this.pausedMaps.put(request.uuid, request)
// runNode = runNode.next;
// }
// this.runningMaps.clear();
// console.log('dodo pauseRequests start2 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
//
// let pendNode = this.pendingMaps.getHead();
// while (pendNode) {
// let request = pendNode.value;
// this.pausedMaps.put(request.uuid, request)
// pendNode = pendNode.next
// }
// this.pendingMaps.clear()
// console.log('dodo pauseRequests start3 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
// })
// }
//
// // 恢复所有被暂停的请求
// async resumeRequests(): Promise<void> {
// await this.mutex.lock(async () => {
// this.isPaused = false;
// console.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
// // 重启了之后需要把paused 里面的所有request重新发送
// let headNode = this.pausedMaps.getHead();
// while (headNode) {
// let request = headNode.value
// this.loadCacheManager(request)
// headNode = headNode.next
// }
// this.pausedMaps.clear()
// console.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
// })
// }
// 删除 请求
remove(uuid: string) {
if (this.isPaused) {
this.pausedMaps.remove(uuid)
} else {
// TODO 哪些请求可以删除
this.pendingMaps.remove(uuid)
this.runningMaps.remove(uuid)
}
}
// 正常加载 // 正常加载
call(request: RequestOption):void { call(request: RequestOption): void {
// 添加全局监听 // 添加全局监听
if(this.defaultListener) { if (this.defaultListener) {
request.addListener(this.defaultListener) request.addListener(this.defaultListener)
} }
@ -231,28 +296,28 @@ export class ImageKnife {
} }
loadResources(request: RequestOption) { loadResources(request: RequestOption) {
let factories:EngineKeyInterface; let factories: EngineKeyInterface;
let cacheKey:string; let cacheKey: string;
let transferKey:string; let transferKey: string;
let dataKey:string; let dataKey: string;
if(this.engineKeyImpl){ if (this.engineKeyImpl) {
factories = this.engineKeyImpl; factories = this.engineKeyImpl;
}else { } else {
factories = new EngineKeyFactories(); factories = new EngineKeyFactories();
} }
// 生成内存缓存key 内存 变换后磁盘 // 生成内存缓存key 内存 变换后磁盘
let loadKey = ''; let loadKey = '';
if(typeof request.loadSrc == 'string'){ if (typeof request.loadSrc == 'string') {
loadKey = request.loadSrc; loadKey = request.loadSrc;
}else{ } else {
loadKey = JSON.stringify(request.loadSrc); loadKey = JSON.stringify(request.loadSrc);
} }
let size = JSON.stringify(request.size); let size = JSON.stringify(request.size);
let transformed = ''; let transformed = '';
if(request && request.transformations) { if (request && request.transformations) {
for (let i = 0; i < request.transformations.length; i++) { for (let i = 0; i < request.transformations.length; i++) {
if (i == request.transformations.length - 1) { if (i == request.transformations.length - 1) {
transformed += request.transformations[i].getName() + ""; transformed += request.transformations[i].getName() + "";
@ -266,6 +331,12 @@ export class ImageKnife {
let signature = request.signature; let signature = request.signature;
if (signature != undefined) {
console.log("唯一标识:" + signature.getKey())
}
cacheKey = factories.generateMemoryCacheKey(loadKey,size,transformed,dontAnimateFlag,signature); cacheKey = factories.generateMemoryCacheKey(loadKey,size,transformed,dontAnimateFlag,signature);
// 生成磁盘缓存变换后数据key 变换后数据保存在磁盘 // 生成磁盘缓存变换后数据key 变换后数据保存在磁盘
@ -283,120 +354,135 @@ export class ImageKnife {
// 删除执行结束的running // 删除执行结束的running
removeRunning(request: RequestOption) { removeRunning(request: RequestOption) {
let index = -1; if (this.isPaused) {
for (let i = 0; i < this.runningRequest.length; i++) {
let tempRunning = this.runningRequest[i]; } else {
if (this.keyEqual(request, tempRunning)) { this.runningMaps.remove(request.uuid);
// 如果key相同 说明找到执行的request我们记录下当前request的index位置 console.log('dodo runningMaps length =' + this.runningMaps.size())
index = i; let previousRequest = request;
break; this.loadNextPending(previousRequest);
}
}
if (index >= 0) {
let request = this.runningRequest.splice(index, 1)[0];
this.loadNextPending(request);
} }
} }
// 执行相同key的pending队列请求 // 执行相同key的pending队列请求
keyEqualPendingToRun(index:number){ private keyEqualPendingToRun(nextPending: RequestOption) {
let nextPending = this.pendingRequest.splice(index, 1)[0]; // let nextPending = this.pendingRequest.splice(index, 1)[0];
this.runningRequest.push(nextPending) // this.runningRequest.push(nextPending)
// RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
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)
} }
searchNextKeyToRun(){ private searchNextKeyToRun() {
// 其次则寻找pending中第一个和running不重复的requestOrKey // 其次则寻找pending中第一个和running不重复的requestOrKey
let index2 = -1; let pendingTailNode = this.pendingMaps.getTail();
for (let i = 0; i < this.pendingRequest.length; i++) { while (pendingTailNode) {
let temppending = this.pendingRequest[i]; let pendingRequest = pendingTailNode.value;
let hasKeyEqual = false; let hasEqual = false;
for (let j = 0; j < this.runningRequest.length; j++) { let runningTailNode = this.runningMaps.getTail();
let temprunning = this.runningRequest[j]; while (runningTailNode) {
if (this.requestOrKeyEqual(temppending, temprunning)) { let runningRequest = runningTailNode.value;
hasKeyEqual = true; if (this.requestOrKeyEqual(pendingRequest, runningRequest)) {
break; hasEqual = true;
break
} }
runningTailNode = runningTailNode.prev;
} }
if (!hasKeyEqual) {
index2 = i; if (hasEqual) {
break; break;
} }
pendingTailNode = pendingTailNode.prev;
} }
if (index2 >= 0) {
let nextPending = this.pendingRequest.splice(index2, 1)[0]; if (pendingTailNode != null && pendingTailNode.value != null) {
this.runningRequest.push(nextPending) 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)
} else {
// 不执行
} }
} }
// 加载下一个key的请求 // 加载下一个key的请求
loadNextPending(request:RequestOption) { private loadNextPending(request: RequestOption) {
// 首先寻找被移除key相同的request let hasEqualRunning = false;
let index = -1; let tailNode = this.pendingMaps.getTail();
for (let i = 0; i < this.pendingRequest.length; i++) { while (tailNode) {
let temppending = this.pendingRequest[i]; if (this.requestOrKeyEqual(request, tailNode.value)) {
if (this.requestOrKeyEqual(request, temppending)) { hasEqualRunning = true;
// 如果key相同 说明目前有任务正在执行我们记录下当前request 放入pendingRunning
index = i;
break; break;
} }
tailNode = tailNode.prev;
} }
if (index >= 0) {
this.keyEqualPendingToRun(index); if (hasEqualRunning) {
if(tailNode != null && tailNode.value != null) {
this.keyEqualPendingToRun(tailNode.value);
}
} else { } else {
this.searchNextKeyToRun(); this.searchNextKeyToRun();
} }
} }
// 启动新线程 去磁盘取 去网络取 // 启动新线程 去磁盘取 去网络取
loadCacheManager(request: RequestOption) { private loadCacheManager(request: RequestOption) {
if (this.keyNotEmpty(request)) { if (this.isPaused) {
let hasRunningRequest = false; // 将当前request存入pausedMaps
for (let i = 0; i < this.runningRequest.length; i++) { this.pausedMaps.put(request.uuid, request);
let tempRunning = this.runningRequest[i]; } else {
if (this.requestOrKeyEqual(request, tempRunning)) { // 正常逻辑
if (this.keyNotEmpty(request)) {
let hasRunningRequest = false;
// 遍历双向链表 从尾巴到头
let tailNode = this.runningMaps.getTail();
while (tailNode) {
if (this.requestOrKeyEqual(request, tailNode.value)) {
hasRunningRequest = true;
break;
}
tailNode = tailNode.prev
}
// 如果requestOrKey相同 说明目前有任务正在执行我们记录下当前request 放入pendingRunning if (hasRunningRequest) {
hasRunningRequest = true; this.pendingMaps.put(request.uuid, request);
break;
// this.pendingRequest.push(request);
} else {
this.runningMaps.put(request.uuid, request)
// this.runningRequest.push(request);
// 不存在相同key的 任务可以并行
RequestManager.execute(request, this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
} }
} }
if (hasRunningRequest) { else {
this.pendingRequest.push(request); LogUtil.log("key没有生成无法进入存取")
} else {
this.runningRequest.push(request);
// 不存在相同key的 任务可以并行
RequestManager.execute(request, this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
} }
} }
else {
LogUtil.log("key没有生成无法进入存取")
}
} }
private keyNotEmpty(request: RequestOption): boolean{ private keyNotEmpty(request: RequestOption): boolean {
if ( if (
request.generateCacheKey != null && request.generateCacheKey.length > 0 && request.generateCacheKey != null && request.generateCacheKey.length > 0 &&
request.generateDataKey != null && request.generateDataKey.length > 0 && request.generateDataKey != null && request.generateDataKey.length > 0 &&
request.generateResourceKey != null && request.generateResourceKey.length > 0 request.generateResourceKey != null && request.generateResourceKey.length > 0
) { ) {
return true; return true;
} }
return false; return false;
} }
private keyEqual(request1: RequestOption, request2: RequestOption): boolean{ private keyEqual(request1: RequestOption, request2: RequestOption): boolean {
// key 完全相等的情况 // key 完全相等的情况
if ( if (
request1.generateCacheKey == request2.generateCacheKey && request1.generateCacheKey == request2.generateCacheKey &&
request1.generateResourceKey == request2.generateResourceKey && request1.generateResourceKey == request2.generateResourceKey &&
request1.generateDataKey == request2.generateDataKey request1.generateDataKey == request2.generateDataKey
) { ) {
return true; return true;
} }
@ -405,18 +491,18 @@ export class ImageKnife {
} }
// 非严格校验模式,如果所有key相等我们认为一定相等, 如果请求类型是string 网络请求url或者uri相等 我们也认为该请求应该只发送一个即可,后续请求会去缓存或者磁盘读取 // 非严格校验模式,如果所有key相等我们认为一定相等, 如果请求类型是string 网络请求url或者uri相等 我们也认为该请求应该只发送一个即可,后续请求会去缓存或者磁盘读取
private requestOrKeyEqual(request1: RequestOption, request2: RequestOption): boolean{ private requestOrKeyEqual(request1: RequestOption, request2: RequestOption): boolean {
// key 完全相等的情况 // key 完全相等的情况
if ( if (
request1.generateCacheKey == request2.generateCacheKey && request1.generateCacheKey == request2.generateCacheKey &&
request1.generateResourceKey == request2.generateResourceKey && request1.generateResourceKey == request2.generateResourceKey &&
request1.generateDataKey == request2.generateDataKey request1.generateDataKey == request2.generateDataKey
) { ) {
return true; return true;
} }
// 如果加载的是网络url或者是本地文件uri读取,那么loadSrc相同就认为是同一个请求 // 如果加载的是网络url或者是本地文件uri读取,那么loadSrc相同就认为是同一个请求
if( if (
typeof request1.loadSrc == 'string' && typeof request2.loadSrc == 'string' && request1.loadSrc == request2.loadSrc typeof request1.loadSrc == 'string' && typeof request2.loadSrc == 'string' && request1.loadSrc == request2.loadSrc
) { ) {
return true; return true;
@ -425,7 +511,7 @@ export class ImageKnife {
return false; return false;
} }
parseSource(request: RequestOption):void { private parseSource(request: RequestOption): void {
if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') { if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') {
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, request.loadSrc as PixelMap) let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, request.loadSrc as PixelMap)
request.loadComplete(imageKnifeData); request.loadComplete(imageKnifeData);

View File

@ -16,7 +16,7 @@
import { ImageKnifeOption } from '../imageknife/ImageKnifeOption' import { ImageKnifeOption } from '../imageknife/ImageKnifeOption'
import { ImageKnifeGlobal } from '../imageknife/ImageKnifeGlobal' import { ImageKnifeGlobal } from '../imageknife/ImageKnifeGlobal'
import { TransformType } from '../imageknife/transform/TransformType' import { TransformType } from '../imageknife/transform/TransformType'
import { RequestOption, Size } from '../imageknife/RequestOption' import { DetachFromLayout, RequestOption, Size } from '../imageknife/RequestOption'
import { ImageKnifeData } from '../imageknife/ImageKnifeData' import { ImageKnifeData } from '../imageknife/ImageKnifeData'
import { GIFFrame } from '../imageknife/utils/gif/GIFFrame' import { GIFFrame } from '../imageknife/utils/gif/GIFFrame'
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle' import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
@ -82,24 +82,30 @@ export struct ImageKnifeComponent {
private onReadyNext?: (data:ImageKnifeData|number|undefined) => void = undefined private onReadyNext?: (data:ImageKnifeData|number|undefined) => void = undefined
private onReadyNextData:ImageKnifeData|number|undefined = undefined private onReadyNextData:ImageKnifeData|number|undefined = undefined
private detachFromLayout:DetachFromLayout|undefined = undefined;
build() { build() {
Canvas(this.context) Canvas(this.context)
.width('100%') .width('100%')
.height('100%') .height('100%')
.onAreaChange((oldValue: Area, newValue: Area) => { .onAreaChange((oldValue: Area, newValue: Area) => {
this.currentWidth = newValue.width as number if(newValue != undefined && newValue.width != undefined && newValue.height != undefined) {
this.currentHeight = newValue.height as number this.currentWidth = newValue.width as number
if (this.currentWidth <= 0 || this.currentHeight <= 0) { this.currentHeight = newValue.height as number
// 存在宽或者高为0,此次重回无意义,无需进行request请求 if (this.currentWidth <= 0 || this.currentHeight <= 0) {
} else { // 存在宽或者高为0,此次重回无意义,无需进行request请求
// 前提:宽高值均有效,值>0. 条件1当前宽高与上一次宽高不同 条件2:当前是第一次绘制 } else {
if ((this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) || this.firstDrawFlag) { // 前提:宽高值均有效,值>0. 条件1当前宽高与上一次宽高不同 条件2:当前是第一次绘制
this.firstDrawFlag = false; if ((this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) || this.firstDrawFlag) {
LogUtil.log('ImageKnifeComponent onAreaChange And Next To Execute. Canvas currentWidth =' + this.currentWidth + ' currentHeight=' + this.currentHeight) this.firstDrawFlag = false;
this.lastWidth = this.currentWidth LogUtil.log('ImageKnifeComponent onAreaChange And Next To Execute. Canvas currentWidth =' + this.currentWidth + ' currentHeight=' + this.currentHeight)
this.lastHeight = this.currentHeight this.lastWidth = this.currentWidth
this.imageKnifeExecute() this.lastHeight = this.currentHeight
this.imageKnifeExecute()
}
} }
}else{
LogUtil.log('ImageKnifeComponent onAreaChange Error newValue is undefined')
} }
}) })
.onReady(() => { .onReady(() => {
@ -275,6 +281,7 @@ export struct ImageKnifeComponent {
} }
this.resetGifData() this.resetGifData()
let request = new RequestOption(); let request = new RequestOption();
this.detachFromLayout = request.detachFromLayout;
this.configNecessary(request); this.configNecessary(request);
this.configCacheStrategy(request); this.configCacheStrategy(request);
this.configDisplay(request); this.configDisplay(request);
@ -421,7 +428,7 @@ export struct ImageKnifeComponent {
drawPlaceholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) { drawPlaceholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
LogUtil.log('ImageKnifeComponent default drawPlaceholder start!') LogUtil.log('ImageKnifeComponent default drawPlaceholder start!')
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => { data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height) LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
let scaleType = (typeof imageKnifeOption.placeholderScaleType == 'number') ? imageKnifeOption.placeholderScaleType : ScaleType.FIT_CENTER let scaleType = (typeof imageKnifeOption.placeholderScaleType == 'number') ? imageKnifeOption.placeholderScaleType : ScaleType.FIT_CENTER
@ -476,7 +483,7 @@ export struct ImageKnifeComponent {
drawThumbSizeMultiplier(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) { drawThumbSizeMultiplier(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
LogUtil.log('ImageKnifeComponent default drawThumbSizeMultiplier start!') LogUtil.log('ImageKnifeComponent default drawThumbSizeMultiplier start!')
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => { data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height) LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
let scaleType = (typeof imageKnifeOption.thumbSizeMultiplierScaleType == 'number') ? imageKnifeOption.thumbSizeMultiplierScaleType : ScaleType.FIT_CENTER let scaleType = (typeof imageKnifeOption.thumbSizeMultiplierScaleType == 'number') ? imageKnifeOption.thumbSizeMultiplierScaleType : ScaleType.FIT_CENTER
@ -491,7 +498,7 @@ export struct ImageKnifeComponent {
drawMainSource(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) { drawMainSource(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
LogUtil.log('ImageKnifeComponent default drawMainSource start!') LogUtil.log('ImageKnifeComponent default drawMainSource start!')
if (data.isPixelMap()) { if (data.isPixelMap()) {
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => { data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height + 'scaleType=' + scaleType) LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height + 'scaleType=' + scaleType)
@ -508,7 +515,7 @@ export struct ImageKnifeComponent {
drawRetryholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) { drawRetryholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
LogUtil.log('ImageKnifeComponent default drawRetryholder start!') LogUtil.log('ImageKnifeComponent default drawRetryholder start!')
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => { data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height) LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
let scaleType = (typeof imageKnifeOption.retryholderScaleType == 'number') ? imageKnifeOption.retryholderScaleType : ScaleType.FIT_CENTER let scaleType = (typeof imageKnifeOption.retryholderScaleType == 'number') ? imageKnifeOption.retryholderScaleType : ScaleType.FIT_CENTER
@ -522,7 +529,7 @@ export struct ImageKnifeComponent {
drawErrorholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) { drawErrorholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
LogUtil.log('ImageKnifeComponent default drawErrorholder start!') LogUtil.log('ImageKnifeComponent default drawErrorholder start!')
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => { data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height) LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
let scaleType = (typeof imageKnifeOption.errorholderSrcScaleType == 'number') ? imageKnifeOption.errorholderSrcScaleType : ScaleType.FIT_CENTER let scaleType = (typeof imageKnifeOption.errorholderSrcScaleType == 'number') ? imageKnifeOption.errorholderSrcScaleType : ScaleType.FIT_CENTER
@ -584,6 +591,10 @@ export struct ImageKnifeComponent {
aboutToDisappear() { aboutToDisappear() {
LogUtil.log('ImageKnifeComponent aboutToDisappear happened!') LogUtil.log('ImageKnifeComponent aboutToDisappear happened!')
if(this.detachFromLayout != undefined){
this.detachFromLayout.detach();
}
this.resetGifData(); this.resetGifData();
} }
@ -601,7 +612,7 @@ export struct ImageKnifeComponent {
} }
private drawLifeCycleHasConsumed<K, T>( methodName: string, private drawLifeCycleHasConsumed<K, T>( methodName: string,
context: CanvasRenderingContext2D, data: K, imageKnifeOption: T, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void,drawLifeCycle?: IDrawLifeCycle context: CanvasRenderingContext2D, data: K, imageKnifeOption: T, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void,drawLifeCycle?: IDrawLifeCycle
):boolean { ):boolean {
if (drawLifeCycle && (drawLifeCycle as Record<string,Function>)[methodName]) { if (drawLifeCycle && (drawLifeCycle as Record<string,Function>)[methodName]) {
return (drawLifeCycle as Record<string,Function>)[methodName](context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId) return (drawLifeCycle as Record<string,Function>)[methodName](context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
@ -677,7 +688,7 @@ export struct ImageKnifeComponent {
// 理论上该帧在屏幕上保留的时间 // 理论上该帧在屏幕上保留的时间
let stayTime:number= 0 let stayTime:number= 0
if(this.renderFrames_frames != undefined) { if(this.renderFrames_frames != undefined) {
stayTime = this.renderFrames_frames[this.renderFrames_index].delay; stayTime = this.renderFrames_frames[this.renderFrames_index].delay;
} }
if (this.imageKnifeOption.gif && this.imageKnifeOption.gif.speedFactory) { if (this.imageKnifeOption.gif && this.imageKnifeOption.gif.speedFactory) {
stayTime = stayTime / (this.imageKnifeOption.gif?.speedFactory * 1.0); stayTime = stayTime / (this.imageKnifeOption.gif?.speedFactory * 1.0);
@ -706,9 +717,9 @@ export struct ImageKnifeComponent {
} }
private drawFrame(frames: GIFFrame[]|undefined, index: number, context: CanvasRenderingContext2D|undefined, compWidth: number, compHeight: number) { private drawFrame(frames: GIFFrame[]|undefined, index: number, context: CanvasRenderingContext2D|undefined, compWidth: number, compHeight: number) {
if(frames == undefined){ if(frames == undefined){
return return
} }
// get current frame // get current frame
let frame = frames[index]; let frame = frames[index];
if (!frame || !context) { if (!frame || !context) {
@ -848,9 +859,9 @@ export class ScaleTypeHelper {
let dy:number = (compHeight - imageHeight * minScale) / (minScale * 1.0); let dy:number = (compHeight - imageHeight * minScale) / (minScale * 1.0);
let dw:number = imageWidth; let dw:number = imageWidth;
let dh:number = imageHeight; let dh:number = imageHeight;
if(source!= undefined) { if(source!= undefined) {
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0)) context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
} }
} }
static drawFitCenter(context: CanvasRenderingContext2D, source: PixelMap | undefined, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) { static drawFitCenter(context: CanvasRenderingContext2D, source: PixelMap | undefined, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
@ -859,9 +870,9 @@ export class ScaleTypeHelper {
let dy:number = (compHeight - imageHeight * minScale) / (minScale * 2.0); let dy:number = (compHeight - imageHeight * minScale) / (minScale * 2.0);
let dw:number = imageWidth; let dw:number = imageWidth;
let dh:number = imageHeight; let dh:number = imageHeight;
if(source!= undefined) { if(source!= undefined) {
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0)) context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
} }
} }
static drawCenter(context: CanvasRenderingContext2D, source: PixelMap | undefined, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) { static drawCenter(context: CanvasRenderingContext2D, source: PixelMap | undefined, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
@ -869,9 +880,9 @@ export class ScaleTypeHelper {
let dy:number = (compHeight - imageHeight) / 2.0; let dy:number = (compHeight - imageHeight) / 2.0;
let dw:number = imageWidth; let dw:number = imageWidth;
let dh:number = imageHeight; let dh:number = imageHeight;
if(source!= undefined) { if(source!= undefined) {
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0)) context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
} }
} }
static drawCenterCrop(context: CanvasRenderingContext2D, source: PixelMap | undefined, maxScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) { static drawCenterCrop(context: CanvasRenderingContext2D, source: PixelMap | undefined, maxScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
@ -880,9 +891,9 @@ export class ScaleTypeHelper {
let dy:number = (compHeight - imageHeight * maxScale) / (maxScale * 2.0); let dy:number = (compHeight - imageHeight * maxScale) / (maxScale * 2.0);
let dw:number = imageWidth; let dw:number = imageWidth;
let dh:number = imageHeight; let dh:number = imageHeight;
if(source!= undefined) { if(source!= undefined) {
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0)) context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
} }
} }
static drawFitXY(context: CanvasRenderingContext2D, source: PixelMap | undefined, scaleW: number, scaleH: number, imageWidth: number, imageHeight: number, imageOffsetX?: number, imageOffsetY?: number) { static drawFitXY(context: CanvasRenderingContext2D, source: PixelMap | undefined, scaleW: number, scaleH: number, imageWidth: number, imageHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
@ -891,9 +902,9 @@ export class ScaleTypeHelper {
let dy:number = 0; let dy:number = 0;
let dw:number = imageWidth; let dw:number = imageWidth;
let dh:number = imageHeight; let dh:number = imageHeight;
if(source!= undefined) { if(source!= undefined) {
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0)) context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
} }
} }
static drawCenterInside(context: CanvasRenderingContext2D, source: PixelMap | undefined, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) { static drawCenterInside(context: CanvasRenderingContext2D, source: PixelMap | undefined, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
@ -912,7 +923,7 @@ export class ScaleTypeHelper {
if(source!= undefined) { if(source!= undefined) {
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0)) context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
} }
} }
} }

View File

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

View File

@ -54,7 +54,12 @@ export interface Size {
width: number, width: number,
height: number height: number
} }
export interface DetachFromLayout{
detach:()=>void
}
export class RequestOption { export class RequestOption {
uuid:string ='' // 唯一标识
loadSrc: string | PixelMap | Resource = ''; loadSrc: string | PixelMap | Resource = '';
strategy: DiskStrategy = new AUTOMATIC(); strategy: DiskStrategy = new AUTOMATIC();
dontAnimateFlag = false; dontAnimateFlag = false;
@ -117,11 +122,30 @@ export class RequestOption {
// 缩略图展示 // 缩略图展示
loadThumbnailReady = false; loadThumbnailReady = false;
detachFromLayout:DetachFromLayout = {
detach: ()=>{
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
if(imageKnife != undefined) {
imageKnife.remove(this.uuid);
}
}
}
constructor() { constructor() {
// 初始化全局监听 // 初始化全局监听
this.requestListeners = new Array(); this.requestListeners = new Array();
// 初始化唯一标识,可以用这个标识找到ImageKnife中的对象
this.uuid = this.generateUUID();
}
generateUUID(): string {
let d = new Date().getTime();
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( new RegExp("[xy]","g"), (c) => {
const r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
return uuid;
} }
/** /**
@ -149,8 +173,6 @@ export class RequestOption {
this.cachesPath = path; this.cachesPath = path;
} }
load(src: string | PixelMap | Resource) { load(src: string | PixelMap | Resource) {
this.loadSrc = src; this.loadSrc = src;
return this; return this;
@ -489,15 +511,15 @@ export class RequestOption {
// 非落盘情况,直接进行寻找下一个加载 // 非落盘情况,直接进行寻找下一个加载
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
if(imageKnife != undefined) { if(imageKnife != undefined) {
imageKnife.removeRunning(this); imageKnife.removeRunning(this);
} }
} }
// 加载成功之后 // // 加载成功之后
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); // let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
if(imageKnife != undefined) { // if(imageKnife != undefined) {
imageKnife.removeRunning(this); // imageKnife.removeRunning(this);
} // }
} }
// 图片文件落盘之后会自动去寻找下一个数据加载 // 图片文件落盘之后会自动去寻找下一个数据加载

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { IDataFetch } from '../networkmanage/IDataFetch'
import { RequestOption } from '../RequestOption'
import { SparkMD5 } from '../../3rd_party/sparkmd5/spark-md5'
import { FileUtils } from '../../cache/FileUtils'
import loadRequest from '@ohos.request';
import { LogUtil } from '../utils/LogUtil'
import { ImageKnifeGlobal } from '../ImageKnifeGlobal'
import common from '@ohos.app.ability.common'
import { BusinessError } from '@ohos.base'
import http from '@ohos.net.http'
// 数据加载器
class RequestData{
receiveSize: number = 2000
totalSize: number = 2000
}
export class HttpDownloadClient implements IDataFetch {
loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) {
try {
let httpRequest = http.createHttp()
let arrayBuffers = new Array<ArrayBuffer>();
httpRequest.on('headersReceive', (header: Object) => {
// 跟服务器连接成功准备下载
if (request.progressFunc) {
// 进度条为0
request.progressFunc.asyncSuccess(0)
}
})
httpRequest.on('dataReceive', (data: ArrayBuffer) => {
// 下载数据流多次返回
arrayBuffers.push(data);
})
httpRequest.on('dataReceiveProgress', (data: RequestData) => {
// 下载进度
if(data != undefined && (typeof data.receiveSize == 'number') && (typeof data.totalSize == 'number') ) {
let percent = Math.round(((data.receiveSize * 1.0) / (data.totalSize * 1.0)) * 100)
if (request.progressFunc) {
request.progressFunc.asyncSuccess(percent)
}
}
})
httpRequest.on('dataEnd', () => {
// 下载完毕
let combineArray = this.combineArrayBuffers(arrayBuffers);
onComplete(combineArray)
})
httpRequest.requestInStream(
request.loadSrc as string,
{
method: http.RequestMethod.GET,
expectDataType: http.HttpDataType.ARRAY_BUFFER,
connectTimeout: 60000, // 可选 默认60000ms
readTimeout: 0, //可选, 默认为60000ms
usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定
},
(err: BusinessError, data: number) => {
if (!err && data == 200) {
} else {
onError(`HttpDownloadClient has error, http code = ${data}`)
}
}
)
} catch (err) {
onError('HttpDownloadClient catch err request uuid ='+request.uuid)
}
}
combineArrayBuffers(arrayBuffers: ArrayBuffer[]): ArrayBuffer {
// 计算多个ArrayBuffer的总字节大小
let totalByteLength = 0;
for (const arrayBuffer of arrayBuffers) {
totalByteLength += arrayBuffer.byteLength;
}
// 创建一个新的ArrayBuffer
const combinedArrayBuffer = new ArrayBuffer(totalByteLength);
// 创建一个Uint8Array来操作新的ArrayBuffer
const combinedUint8Array = new Uint8Array(combinedArrayBuffer);
// 依次复制每个ArrayBuffer的内容到新的ArrayBuffer中
let offset = 0;
for (const arrayBuffer of arrayBuffers) {
const sourceUint8Array = new Uint8Array(arrayBuffer);
combinedUint8Array.set(sourceUint8Array, offset);
offset += sourceUint8Array.length;
}
return combinedArrayBuffer;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,143 +12,90 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import {LogUtil} from '../../imageknife/utils/LogUtil' import { LogUtil } from '../../imageknife/utils/LogUtil'
// jpg = 'jpg,0,FFD8',
// png = 'png,0,89504E470D0A1A0A',
// bmp = 'bmp,0,424D',
// gif = 'gif,0,474946383961',
// svg = 'svg,0,3C3F786D6C',
// webp = 'webp,0,52494646',
// tiff = 'tiff,0,492049|49492A00|4D4D002A|4D4D002B'
export class FileTypeUtil { export class FileTypeUtil {
private map:Map<string,string> = new Map<string,string>(); private fileSignatureMap: Record<string, Array<Uint8Array>> = {
private READ_MIN_LENGTH:number = 0; // 添加文件类型和对应的文件头部特征
private SUPPORT_FORMATS = [ 'jpg': [new Uint8Array([0xFF, 0xD8])],
PhotoFormat.jpg, 'png': [new Uint8Array([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])],
PhotoFormat.png, 'gif': [new Uint8Array([0x47, 0x49, 0x46, 0x38, 0x39, 0x61])],
PhotoFormat.tiff, 'bmp': [new Uint8Array([0x42, 0x4D])],
PhotoFormat.bmp, 'svg': [new Uint8Array([0x3C, 0x3F, 0x78, 0x6D, 0x6C])],
PhotoFormat.webp, 'webp': [new Uint8Array([0x52, 0x49, 0x46, 0x46])],
PhotoFormat.svg, 'tiff': [new Uint8Array([0x49, 0x20, 0x49]), new Uint8Array([0x49, 0x49, 0x2A, 0x00]), new Uint8Array([0x4D, 0x4D, 0x00, 0x2A]), new Uint8Array([0x4D, 0x4D, 0x00, 0x2B])],
PhotoFormat.gif // 添加更多的文件类型和特征
] };
constructor() { constructor() {
this.initImageType();
} }
private getDataViewAt(dataView:DataView,index:number): string{ isImage(arraybuffer: ArrayBuffer) {
return this.dec2Hex(dataView.getUint8(index))
}
getFileType(arraybuffer: ArrayBuffer):string {
let fileType:string = '';
if (arraybuffer == null || arraybuffer == undefined || arraybuffer.byteLength <= this.READ_MIN_LENGTH) {
return '';
}
let dataView = new DataView(arraybuffer);
LogUtil.log('dataView +'+this.getDataViewAt(dataView,0)+this.getDataViewAt(dataView,1))
let entries: IterableIterator<Object[]> = this.map.entries();
for (let i = 0; i < this.map.size; i++) {
let entry:Object[] = entries.next().value;
let key = entry[0] as string
let value = entry[1] as string
let keySplit = key.split(',')
if(keySplit.length == 2){
let offset = Number(keySplit[0])
let magicStringLength = keySplit[1].length;
let readLength = magicStringLength/2;
let start = 0;
let fileMagic = ''
while(start< readLength){
fileMagic+=this.getDataViewAt(dataView,offset+start)
start++;
}
if(fileMagic == keySplit[1]){
LogUtil.log('匹配到了 fileType='+value)
fileType = value
break;
}
}
}
return fileType;
}
dec2Hex(uint8: number) {
let hex = uint8.toString(16);
if (hex.length <= 1) {
hex = "0" + hex;
}
return hex.toUpperCase();
}
private initImageType() {
if(!this.map){
this.map = new Map()
}
this.SUPPORT_FORMATS.forEach((value,index,arrs)=>{
let values = value.split(',')
if(values.length == 3){
let magicSplits = values[2].split("|")
if(magicSplits.length == 1){
this.map.set(values[1]+','+values[2],values[0])
}else if(magicSplits.length > 1){
for(let i=0; i<magicSplits.length; i++){
let magicStr = magicSplits[i];
this.map.set(values[1]+','+magicStr,values[0])
}
}else{
// 文件魔数不存在,不处理
}
}
})
this.initReadMinLength();
this.printMapContent();
}
private printMapContent(){
let entries: IterableIterator<Object[]> = this.map.entries();
for (let i = 0; i < this.map.size; i++) {
let entry:Object[] = entries.next().value;
let key = entry[0] as string
let value = entry[1] as string
LogUtil.log('key='+key+'---value='+value)
}
}
private initReadMinLength(){
let max = 0;
this.map.forEach((value,key,map)=>{
let keySplit = key.split(',');
if(keySplit.length == 2){
let offset = Number(keySplit[0])
let magicStringLength = keySplit[1].length;
let tempMax = offset + magicStringLength/2;
if(tempMax > max){
max = tempMax;
}
}
})
this.READ_MIN_LENGTH =max;
}
isImage(arraybuffer:ArrayBuffer){
let value = this.getFileType(arraybuffer); let value = this.getFileType(arraybuffer);
if( if (
value == SupportFormat.jpg || value == SupportFormat.jpg ||
value == SupportFormat.png || value == SupportFormat.png ||
value == SupportFormat.tiff || value == SupportFormat.tiff ||
value == SupportFormat.webp || value == SupportFormat.webp ||
value == SupportFormat.bmp || value == SupportFormat.bmp ||
value == SupportFormat.gif || value == SupportFormat.gif ||
value == SupportFormat.svg value == SupportFormat.svg
){ ) {
return true; return true;
} }
return false; return false;
} }
getFileType(file: ArrayBuffer): string | null {
const fileData = new Uint8Array(file);
let hasMatched = false;
let matchedFileType = ''
Object.keys(this.fileSignatureMap).map((fileType)=>{
if(!hasMatched) {
const bufferList = this.fileSignatureMap[fileType];
for (let i = 0; i < bufferList.length; i++) {
let signature = bufferList[i];
if (this.matchesSignature(fileData, signature)) {
hasMatched = true;
matchedFileType = fileType;
break
}
}
}else{
// 由于map函数会输出所有的keys, 所以匹配之后不再需要执行任何逻辑
}
})
if(hasMatched){
return matchedFileType;
}
return null; // 若无法识别文件类型返回null
}
matchesSignature(fileData: Uint8Array, signature: Uint8Array): boolean {
if (fileData.length < signature.length) {
return false; // 文件长度不足,无法匹配魔数
}
for (let i = 0; i < signature.length; i++) {
if (fileData[i] !== signature[i]) {
return false; // 魔数不匹配
}
}
return true; // 文件头部魔数匹配
}
} }
export enum PhotoFormat { export enum PhotoFormat {
jpg = 'jpg,0,FFD8', jpg = 'jpg,0,FFD8',
png = 'png,0,89504E470D0A1A0A', png = 'png,0,89504E470D0A1A0A',

View File

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

View File

@ -0,0 +1,134 @@
/*
* 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.
*/
class Node<K, V> {
key: K;
value: V;
next: Node<K, V> | null;
prev: Node<K, V> | null;
constructor(key: K, value: V) {
this.key = key;
this.value = value;
this.next = null;
this.prev = null;
}
}
// 链表的tail是最近访问,head是最晚访问,对顺序有要求需要访问tail
export class EasyLinkedHashMap<K, V> {
private map: Map<K, Node<K, V>>; // 存储键值对的哈希映射
private head: Node<K, V> | null; // 链表头节点
private tail: Node<K, V> | null; // 链表尾节点
constructor() {
this.map = new Map<K, Node<K, V>>();
this.head = null;
this.tail = null;
}
// 添加键值对到映射中,并在链表尾部添加新节点
put(key: K, value: V) {
if (this.map.has(key)) {
// 如果键已存在,则更新对应的值
const node = this.map.get(key)!;
node.value = value;
this.moveToTail(node);
} else {
// 创建新节点并加入链表尾部
const newNode = new Node(key, value);
this.map.set(key, newNode);
if (this.tail) {
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
} else {
this.head = newNode;
this.tail = newNode;
}
}
}
// 从映射中移除键值对,并从链表中删除对应节点
remove(key: K) {
if (this.map.has(key)) {
const node = this.map.get(key)!;
this.map.delete(key);
this.removeNode(node);
}
}
// 获取键对应的值,并将对应节点移到链表尾部(表示最近访问)
get(key: K): V | undefined {
if (this.map.has(key)) {
const node = this.map.get(key)!;
this.moveToTail(node);
return node.value;
}
return undefined;
}
// 将节点移动到链表尾部
private moveToTail(node: Node<K, V>) {
if (node === this.tail) {
return; // 节点已在链表尾部,无需移动
}
if (node === this.head) {
this.head = node.next;
} else {
node.prev!.next = node.next;
}
node.next!.prev = node.prev;
this.tail!.next = node;
node.prev = this.tail;
node.next = null;
this.tail = node;
}
// 从链表中删除节点
private removeNode(node: Node<K, V>) {
if (node === this.head) {
this.head = node.next;
} else {
node.prev!.next = node.next;
}
if (node === this.tail) {
this.tail = node.prev;
} else {
node.next!.prev = node.prev;
}
}
// 返回映射中的键值对数量
size(): number {
return this.map.size;
}
// 返回顺序双向链表
getTail(){
return this.tail;
}
getHead(){
return this.head;
}
clear(){
this.map.clear()
this.head = null;
this.tail=null;
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.
*/
export class MethodMutex {
private mutex: Promise<void>;
private release: () => void;
constructor() {
this.mutex = Promise.resolve();
this.release = () => {};
}
async lock<T>(fn: () => Promise<T>): Promise<T> {
await this.mutex;
let result: T;
try {
this.mutex = new Promise<void>((resolve) => {
this.release = resolve;
});
result = await fn();
} finally {
this.release();
}
return result;
}
}

View File

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