1.新增MemoryLruCache主动调用PixelMap的release方法,释放native的PixelMap内存
2.新增ImageSource主动调用release方法释放native持有的ImageSource内存 Signed-off-by: zhoulisheng <635547767@qq.com>
This commit is contained in:
parent
fcec94020d
commit
3077af9dc3
|
@ -3,7 +3,7 @@
|
|||
"bundleName": "com.openharmony.imageknife",
|
||||
"vendor": "example",
|
||||
"versionCode": 1000000,
|
||||
"versionName": "2.1.1-rc.0",
|
||||
"versionName": "2.1.1-rc.1",
|
||||
"icon": "$media:app_icon",
|
||||
"label": "$string:app_name",
|
||||
"distributedNotificationEnabled": true
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
## 2.1.1-rc.1
|
||||
|
||||
- 新增自定义key参数配置
|
||||
- 新增MemoryLruCache主动调用PixelMap的release方法,释放native的PixelMap内存
|
||||
- 新增ImageSource主动调用release方法释放native持有的ImageSource内存
|
||||
|
||||
## 2.1.1-rc.0
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ ohpm install @ohos/imageknife
|
|||
"author": "",
|
||||
"license": "",
|
||||
"dependencies": {
|
||||
"@ohos/imageknife": "^2.0.6"
|
||||
"@ohos/imageknife": "^2.1.1-rc.1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"name": "entry",
|
||||
"description": "example description",
|
||||
"repository": {},
|
||||
"version": "2.1.1-rc.0",
|
||||
"version": "2.1.1-rc.1",
|
||||
"dependencies": {
|
||||
"@ohos/imageknife": "file:../imageknife",
|
||||
"@ohos/disklrucache": "^2.0.2-rc.0"
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
import { EngineKeyFactories, EngineKeyInterface, RequestOption } from '@ohos/imageknife'
|
||||
import { ObjectKey } from '@ohos/imageknife/src/main/ets/components/imageknife/ObjectKey';
|
||||
import { ObjectKey } from '@ohos/imageknife';
|
||||
|
||||
export class CustomEngineKeyImpl implements EngineKeyInterface {
|
||||
redefineUrl: (loadSrc: string) => string;
|
||||
|
|
|
@ -23,8 +23,10 @@ struct basicTestFileIOPage {
|
|||
@State filePath: string = '查看featureAbility路径';
|
||||
appFilePath = '';
|
||||
appCachePath = '';
|
||||
@State imageHint: string = ''
|
||||
@State imageFile: string = '文字提醒'
|
||||
@State imageHint: string = '文字提醒1'
|
||||
@State imageHint2: string = '文字提醒2'
|
||||
@State imageFile: string = ''
|
||||
|
||||
@State imageRes: Resource = $r('app.media.pngSample')
|
||||
@State imagePixelMap?: PixelMap = undefined
|
||||
@State normalPixelMap: boolean = false;
|
||||
|
@ -63,7 +65,7 @@ struct basicTestFileIOPage {
|
|||
.margin({ top: 10 })
|
||||
.onClick(() => {
|
||||
if(this.appFilePath == '' || this.appFilePath == null){
|
||||
this.appFilePath = 'appFilePath未取到值,请按顺序从上往下,从左往右依次测试'
|
||||
this.imageHint = 'appFilePath未取到值,请按顺序从上往下,从左往右依次测试'
|
||||
return
|
||||
}
|
||||
console.log('files目录创建Folder1和Folder2 验证statSync mkdirSync')
|
||||
|
@ -94,12 +96,13 @@ struct basicTestFileIOPage {
|
|||
console.log('basicTestFileIOPage - 本地加载资源err' + JSON.stringify(err as BusinessError));
|
||||
})
|
||||
})
|
||||
Text(this.imageHint2)
|
||||
Button('copy:Folder1至Folder2, 验证copyFileSync')
|
||||
.margin({ top: 10 })
|
||||
.onClick(() => {
|
||||
console.log('copy:Folder1至Folder2, 验证copyFileSync')
|
||||
if(this.appFilePath == '' || this.appFilePath == null){
|
||||
this.appFilePath = 'appFilePath未取到值,请按顺序从上往下,从左往右依次测试'
|
||||
this.imageHint2 = 'appFilePath未取到值,请按顺序从上往下,从左往右依次测试'
|
||||
return
|
||||
}
|
||||
let filePath1 = this.appFilePath + '/Folder1/jpgSample.gif';
|
||||
|
|
|
@ -41,7 +41,9 @@ struct BasicTestResourceManagerPage {
|
|||
let arrayBuffer = this.typedArrayToBuffer(data);
|
||||
let filetypeUtil = new FileTypeUtil();
|
||||
let fileType = filetypeUtil.getFileType(arrayBuffer);
|
||||
this.fileTypeStr = fileType;
|
||||
if(fileType != null) {
|
||||
this.fileTypeStr = fileType;
|
||||
}
|
||||
})
|
||||
.catch((err:BusinessError) => {
|
||||
console.log('basicTestFileIOPage - 本地加载资源err' + JSON.stringify(err));
|
||||
|
@ -63,7 +65,9 @@ struct BasicTestResourceManagerPage {
|
|||
.decode(data);
|
||||
let filetypeUtil = new FileTypeUtil();
|
||||
let fileType = filetypeUtil.getFileType(arrayBuffer);
|
||||
this.fileTypeStr = fileType;
|
||||
if(fileType != null) {
|
||||
this.fileTypeStr = fileType;
|
||||
}
|
||||
})
|
||||
.catch((err:BusinessError) => {
|
||||
console.log('basicTestFileIOPage - 本地加载资源err' + JSON.stringify(err));
|
||||
|
|
|
@ -31,16 +31,35 @@ struct ManyPhotoShowPage {
|
|||
build() {
|
||||
Column() {
|
||||
|
||||
Button('设置磁盘存储为50M')
|
||||
.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: "设置成功" })
|
||||
}
|
||||
}
|
||||
})
|
||||
// Button('点击暂停加载')
|
||||
// .margin({top:10,bottom:5})
|
||||
// .onClick(()=>{
|
||||
// let imageKnife = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||
// if(imageKnife!= undefined){
|
||||
// imageKnife.pauseRequests()
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// Button('点击重新加载')
|
||||
// .margin({top:10,bottom:5})
|
||||
// .onClick(()=>{
|
||||
// let imageKnife = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||
// if(imageKnife!= undefined){
|
||||
// imageKnife.resumeRequests()
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// Button('设置磁盘存储为50M')
|
||||
// .margin({top:10,bottom:5})
|
||||
// .onClick(()=>{
|
||||
// if(ImageKnifeGlobal.getInstance().getImageKnife() != undefined) {
|
||||
// let disk: DiskLruCache | undefined = (ImageKnifeGlobal.getInstance().getImageKnife())?.getDiskMemoryCache();
|
||||
// if(disk != undefined) {
|
||||
// disk.setMaxSize(50 * 1024 * 1024)
|
||||
// Prompt.showToast({ message: "设置成功" })
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
List({ space: 20, scroller: this.elementScroller }) {
|
||||
LazyForEach(this.data, (item: Material, index) => {
|
||||
|
|
|
@ -120,7 +120,8 @@ struct PngjTestCasePage {
|
|||
name: 'readPngImageAsync'
|
||||
})
|
||||
pngj.readPngImageAsync(png_worker, this.pngSource2!, {pngCallback: (sender:ArrayBuffer, value:Record<string,Object>) => {
|
||||
this.pngSource1 = sender
|
||||
this.pngSource2 = sender
|
||||
this.hint8 = '重新获取buffer才能测试'
|
||||
this.hint2 = 'img with=' + value.width + ' img height=' + value.height
|
||||
+ ' img depth=' + value.depth + ' img ctype=' + value.ctype
|
||||
this.pngdecodeRun2 = false;
|
||||
|
|
|
@ -17,7 +17,7 @@ import { CropCircleTransformation } from '@ohos/imageknife'
|
|||
import { RoundedCornersTransformation } from '@ohos/imageknife'
|
||||
import {
|
||||
CropCircleWithBorderTransformation
|
||||
} from '@ohos/imageknife/src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation'
|
||||
} from '@ohos/imageknife'
|
||||
import { RotateImageTransformation } from '@ohos/imageknife'
|
||||
import { CropSquareTransformation } from '@ohos/imageknife'
|
||||
import { CropTransformation } from '@ohos/imageknife'
|
||||
|
|
|
@ -95,6 +95,7 @@ export { UPNG } from './src/main/ets/components/3rd_party/upng/UPNG'
|
|||
*/
|
||||
export { ImageKnife } from './src/main/ets/components/imageknife/ImageKnife'
|
||||
export { ImageKnifeGlobal } from './src/main/ets/components/imageknife/ImageKnifeGlobal'
|
||||
export { ObjectKey } from './src/main/ets/components/imageknife/ObjectKey'
|
||||
export {RequestOption,Size} from './src/main/ets/components/imageknife/RequestOption'
|
||||
export { ImageKnifeComponent, ScaleType, ScaleTypeHelper } from './src/main/ets/components/imageknife/ImageKnifeComponent'
|
||||
export { ImageKnifeDrawFactory } from './src/main/ets/components/imageknife/ImageKnifeDrawFactory'
|
||||
|
|
|
@ -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--
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -14,63 +14,71 @@
|
|||
*/
|
||||
|
||||
import { DiskLruCache } from "@ohos/disklrucache"
|
||||
import { LruCache } from "../cache/LruCache"
|
||||
import {EngineKeyFactories} from "../cache/key/EngineKeyFactories"
|
||||
import {EngineKeyInterface} from "../cache/key/EngineKeyInterface"
|
||||
import {RequestOption} from "../imageknife/RequestOption"
|
||||
import {AsyncCallback} from "../imageknife/interface/AsyncCallback"
|
||||
import {PlaceHolderManager} from "../imageknife/holder/PlaceHolderManager"
|
||||
import {RetryHolderManager} from "../imageknife/holder/RetryHolderManager"
|
||||
import {ErrorHolderManager} from "../imageknife/holder/ErrorHolderManager"
|
||||
import {RequestManager} from "../imageknife/requestmanage/RequestManager"
|
||||
import {NONE} from "../cache/diskstrategy/enum/NONE"
|
||||
import {FileTypeUtil} from '../imageknife/utils/FileTypeUtil'
|
||||
import {DownloadClient} from '../imageknife/networkmanage/DownloadClient'
|
||||
import {IDataFetch} from '../imageknife/networkmanage/IDataFetch'
|
||||
import {ParseResClient} from '../imageknife/resourcemanage/ParseResClient'
|
||||
import {IResourceFetch} from '../imageknife/resourcemanage/IResourceFetch'
|
||||
import {ImageKnifeData,ImageKnifeType} from '../imageknife/ImageKnifeData'
|
||||
import {ImageKnifeGlobal} from '../imageknife/ImageKnifeGlobal'
|
||||
import { EngineKeyFactories } from "../cache/key/EngineKeyFactories"
|
||||
import { EngineKeyInterface } from "../cache/key/EngineKeyInterface"
|
||||
import { RequestOption } from "../imageknife/RequestOption"
|
||||
import { AsyncCallback } from "../imageknife/interface/AsyncCallback"
|
||||
import { PlaceHolderManager } from "../imageknife/holder/PlaceHolderManager"
|
||||
import { RetryHolderManager } from "../imageknife/holder/RetryHolderManager"
|
||||
import { ErrorHolderManager } from "../imageknife/holder/ErrorHolderManager"
|
||||
import { RequestManager } from "../imageknife/requestmanage/RequestManager"
|
||||
import { NONE } from "../cache/diskstrategy/enum/NONE"
|
||||
import { FileTypeUtil } from '../imageknife/utils/FileTypeUtil'
|
||||
import { DownloadClient } from '../imageknife/networkmanage/DownloadClient'
|
||||
import { IDataFetch } from '../imageknife/networkmanage/IDataFetch'
|
||||
import { ParseResClient } from '../imageknife/resourcemanage/ParseResClient'
|
||||
import { IResourceFetch } from '../imageknife/resourcemanage/IResourceFetch'
|
||||
import { ImageKnifeData, ImageKnifeType } from '../imageknife/ImageKnifeData'
|
||||
import { ImageKnifeGlobal } from '../imageknife/ImageKnifeGlobal'
|
||||
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 {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 common from '@ohos.app.ability.common'
|
||||
import HashMap from '@ohos.util.HashMap'
|
||||
import LinkedList from '@ohos.util.LinkedList'
|
||||
import { MemoryLruCache } from '../cache/MemoryLruCache'
|
||||
|
||||
export class ImageKnife {
|
||||
static readonly SEPARATOR: string = '/'
|
||||
|
||||
memoryCache: LruCache<string, ImageKnifeData>;
|
||||
diskMemoryCache: DiskLruCache;
|
||||
dataFetch: IDataFetch;
|
||||
resourceFetch: IResourceFetch<ArrayBuffer>;
|
||||
filesPath: string = ""; // data/data/包名/files目录
|
||||
memoryCache: MemoryLruCache;
|
||||
diskMemoryCache: DiskLruCache;
|
||||
dataFetch: IDataFetch;
|
||||
resourceFetch: IResourceFetch<ArrayBuffer>;
|
||||
filesPath: string = ""; // data/data/包名/files目录
|
||||
|
||||
|
||||
placeholderCache: string = "placeholderCache"
|
||||
runningRequest: Array<RequestOption>;
|
||||
pendingRequest: Array<RequestOption>;
|
||||
fileTypeUtil: FileTypeUtil; // 通用文件格式辨别
|
||||
diskCacheFolder: string = "ImageKnifeDiskCache"
|
||||
|
||||
|
||||
defaultListener: AsyncCallback<ImageKnifeData> = {
|
||||
callback:(err: string, data: ImageKnifeData)=>{return false}
|
||||
placeholderCache: string = "placeholderCache"
|
||||
runningMaps: EasyLinkedHashMap<string, RequestOption>;
|
||||
pendingMaps: EasyLinkedHashMap<string, RequestOption>;
|
||||
pausedMaps: EasyLinkedHashMap<string, RequestOption>;
|
||||
isPaused: boolean = false;
|
||||
mutex: MethodMutex = new MethodMutex();
|
||||
fileTypeUtil: FileTypeUtil; // 通用文件格式辨别
|
||||
diskCacheFolder: string = "ImageKnifeDiskCache"
|
||||
defaultListener: AsyncCallback<ImageKnifeData> = {
|
||||
callback: (err: string, data: ImageKnifeData) => {
|
||||
return false
|
||||
}
|
||||
}; // 全局监听器
|
||||
|
||||
// gifWorker
|
||||
gifWorker: worker.ThreadWorker|undefined = undefined;
|
||||
|
||||
defaultLifeCycle: IDrawLifeCycle|undefined = undefined;
|
||||
|
||||
gifWorker: worker.ThreadWorker | undefined = undefined;
|
||||
defaultLifeCycle: IDrawLifeCycle | undefined = undefined;
|
||||
// 开发者可配置全局缓存
|
||||
engineKeyImpl: EngineKeyInterface;
|
||||
engineKeyImpl: EngineKeyInterface;
|
||||
|
||||
private constructor() {
|
||||
|
||||
this.runningMaps = new EasyLinkedHashMap();
|
||||
this.pendingMaps = new EasyLinkedHashMap();
|
||||
this.pausedMaps = new EasyLinkedHashMap();
|
||||
|
||||
// 构造方法传入size 为保存文件个数
|
||||
this.memoryCache = new LruCache<string, ImageKnifeData>(100);
|
||||
this.memoryCache = new MemoryLruCache(100);
|
||||
|
||||
// 创建disk缓存 传入的size 为多少比特 比如20KB 传入20*1024
|
||||
this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext());
|
||||
|
@ -84,22 +92,21 @@ export class ImageKnife {
|
|||
// 初始化本地 文件保存
|
||||
this.filesPath = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).filesDir as string;
|
||||
|
||||
this.runningRequest = new Array();
|
||||
this.pendingRequest = new Array();
|
||||
|
||||
// 通用文件格式识别初始化
|
||||
this.fileTypeUtil = new FileTypeUtil();
|
||||
|
||||
this.engineKeyImpl = new EngineKeyFactories();
|
||||
|
||||
|
||||
}
|
||||
|
||||
getMemoryCache(): LruCache<string, ImageKnifeData>{
|
||||
getMemoryCache(): MemoryLruCache {
|
||||
return this.memoryCache;
|
||||
}
|
||||
|
||||
public static with(context:Object): ImageKnifeGlobal{
|
||||
public static with(context: Object): ImageKnifeGlobal {
|
||||
// 存入hapContext;
|
||||
let global:ImageKnifeGlobal = ImageKnifeGlobal.getInstance();
|
||||
let global: ImageKnifeGlobal = ImageKnifeGlobal.getInstance();
|
||||
global.setHapContext(context)
|
||||
|
||||
// 初始化ImageKnife
|
||||
|
@ -113,7 +120,7 @@ export class ImageKnife {
|
|||
return global;
|
||||
}
|
||||
|
||||
getDiskMemoryCache(): DiskLruCache{
|
||||
getDiskMemoryCache(): DiskLruCache {
|
||||
return this.diskMemoryCache;
|
||||
};
|
||||
|
||||
|
@ -121,7 +128,7 @@ export class ImageKnife {
|
|||
this.diskMemoryCache = diskLruCache;
|
||||
};
|
||||
|
||||
getFileTypeUtil(): FileTypeUtil{
|
||||
getFileTypeUtil(): FileTypeUtil {
|
||||
return this.fileTypeUtil;
|
||||
}
|
||||
|
||||
|
@ -129,7 +136,7 @@ export class ImageKnife {
|
|||
return ImageKnifeGlobal.getInstance().getHapContext();
|
||||
}
|
||||
|
||||
setMemoryCache(lrucache: LruCache<string, ImageKnifeData>) {
|
||||
setMemoryCache(lrucache: MemoryLruCache) {
|
||||
this.memoryCache = lrucache;
|
||||
}
|
||||
|
||||
|
@ -137,22 +144,23 @@ export class ImageKnife {
|
|||
return this.defaultListener;
|
||||
}
|
||||
|
||||
setGifWorker(worker:worker.ThreadWorker){
|
||||
setGifWorker(worker: worker.ThreadWorker) {
|
||||
this.gifWorker = worker
|
||||
}
|
||||
getGifWorker(){
|
||||
|
||||
getGifWorker() {
|
||||
return this.gifWorker;
|
||||
}
|
||||
|
||||
getDefaultLifeCycle(){
|
||||
getDefaultLifeCycle() {
|
||||
return this.defaultLifeCycle;
|
||||
}
|
||||
|
||||
setDefaultLifeCycle(viewLifeCycle:IDrawLifeCycle){
|
||||
setDefaultLifeCycle(viewLifeCycle: IDrawLifeCycle) {
|
||||
this.defaultLifeCycle = viewLifeCycle;
|
||||
}
|
||||
|
||||
setEngineKeyImpl(impl:EngineKeyInterface){
|
||||
setEngineKeyImpl(impl: EngineKeyInterface) {
|
||||
this.engineKeyImpl = impl;
|
||||
}
|
||||
|
||||
|
@ -162,34 +170,34 @@ export class ImageKnife {
|
|||
this.defaultListener = newDefaultListener;
|
||||
}
|
||||
|
||||
public compressBuilder(): CompressBuilder{
|
||||
public compressBuilder(): CompressBuilder {
|
||||
return new CompressBuilder();
|
||||
}
|
||||
|
||||
// 替代原来的LruCache
|
||||
public replaceLruCache(size:number){
|
||||
if(this.memoryCache.map.size() <= 0) {
|
||||
this.memoryCache = new LruCache<string, ImageKnifeData>(size);
|
||||
}else{
|
||||
let newLruCache = new LruCache<string, ImageKnifeData>(size);
|
||||
this.memoryCache.foreachLruCache( (value:ImageKnifeData, key:string, map:Object)=> {
|
||||
public replaceLruCache(size: number) {
|
||||
if (this.memoryCache.map.size() <= 0) {
|
||||
this.memoryCache = new MemoryLruCache(size);
|
||||
} else {
|
||||
let newLruCache = new MemoryLruCache(size);
|
||||
this.memoryCache.foreachLruCache((value: ImageKnifeData, key: string, map: Object) => {
|
||||
newLruCache.put(key, value);
|
||||
})
|
||||
this.memoryCache = newLruCache;
|
||||
}
|
||||
}
|
||||
|
||||
public replaceDataFetch(fetch:IDataFetch){
|
||||
public replaceDataFetch(fetch: IDataFetch) {
|
||||
this.dataFetch = fetch;
|
||||
}
|
||||
|
||||
// 替代原来的DiskLruCache
|
||||
public replaceDiskLruCache(size:number) {
|
||||
public replaceDiskLruCache(size: number) {
|
||||
if (this.diskMemoryCache.getCacheMap().size() <= 0) {
|
||||
this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size);
|
||||
} else {
|
||||
let newDiskLruCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size);
|
||||
this.diskMemoryCache.foreachDiskLruCache( (value:string|ArrayBuffer, key:string, map:Object)=> {
|
||||
this.diskMemoryCache.foreachDiskLruCache((value: string | ArrayBuffer, key: string, map: Object) => {
|
||||
newDiskLruCache.set(key, value);
|
||||
})
|
||||
this.diskMemoryCache = newDiskLruCache;
|
||||
|
@ -197,17 +205,74 @@ export class ImageKnife {
|
|||
}
|
||||
|
||||
// 预加载 resource资源一级缓存,string资源实现二级缓存
|
||||
preload(request: RequestOption):void {
|
||||
preload(request: RequestOption): void {
|
||||
// 每个request 公共信息补充
|
||||
request.setFilesPath(this.filesPath);
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -231,28 +296,28 @@ export class ImageKnife {
|
|||
}
|
||||
|
||||
loadResources(request: RequestOption) {
|
||||
let factories:EngineKeyInterface;
|
||||
let cacheKey:string;
|
||||
let transferKey:string;
|
||||
let dataKey:string;
|
||||
if(this.engineKeyImpl){
|
||||
let factories: EngineKeyInterface;
|
||||
let cacheKey: string;
|
||||
let transferKey: string;
|
||||
let dataKey: string;
|
||||
if (this.engineKeyImpl) {
|
||||
factories = this.engineKeyImpl;
|
||||
}else {
|
||||
} else {
|
||||
factories = new EngineKeyFactories();
|
||||
}
|
||||
// 生成内存缓存key 内存 变换后磁盘
|
||||
|
||||
let loadKey = '';
|
||||
if(typeof request.loadSrc == 'string'){
|
||||
if (typeof request.loadSrc == 'string') {
|
||||
loadKey = request.loadSrc;
|
||||
}else{
|
||||
} else {
|
||||
loadKey = JSON.stringify(request.loadSrc);
|
||||
}
|
||||
|
||||
let size = JSON.stringify(request.size);
|
||||
|
||||
let transformed = '';
|
||||
if(request && request.transformations) {
|
||||
if (request && request.transformations) {
|
||||
for (let i = 0; i < request.transformations.length; i++) {
|
||||
if (i == request.transformations.length - 1) {
|
||||
transformed += request.transformations[i].getName() + "";
|
||||
|
@ -266,6 +331,12 @@ export class ImageKnife {
|
|||
|
||||
let signature = request.signature;
|
||||
|
||||
if (signature != undefined) {
|
||||
console.log("唯一标识:" + signature.getKey())
|
||||
}
|
||||
|
||||
|
||||
|
||||
cacheKey = factories.generateMemoryCacheKey(loadKey,size,transformed,dontAnimateFlag,signature);
|
||||
|
||||
// 生成磁盘缓存变换后数据key 变换后数据保存在磁盘
|
||||
|
@ -283,120 +354,135 @@ export class ImageKnife {
|
|||
|
||||
// 删除执行结束的running
|
||||
removeRunning(request: RequestOption) {
|
||||
let index = -1;
|
||||
for (let i = 0; i < this.runningRequest.length; i++) {
|
||||
let tempRunning = this.runningRequest[i];
|
||||
if (this.keyEqual(request, tempRunning)) {
|
||||
// 如果key相同 说明找到执行的request,我们记录下当前request的index位置
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
let request = this.runningRequest.splice(index, 1)[0];
|
||||
this.loadNextPending(request);
|
||||
if (this.isPaused) {
|
||||
|
||||
} else {
|
||||
this.runningMaps.remove(request.uuid);
|
||||
console.log('dodo runningMaps length =' + this.runningMaps.size())
|
||||
let previousRequest = request;
|
||||
this.loadNextPending(previousRequest);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行相同key的pending队列请求
|
||||
keyEqualPendingToRun(index:number){
|
||||
let nextPending = this.pendingRequest.splice(index, 1)[0];
|
||||
this.runningRequest.push(nextPending)
|
||||
private keyEqualPendingToRun(nextPending: RequestOption) {
|
||||
// let nextPending = this.pendingRequest.splice(index, 1)[0];
|
||||
// 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)
|
||||
|
||||
}
|
||||
|
||||
searchNextKeyToRun(){
|
||||
private searchNextKeyToRun() {
|
||||
// 其次则寻找pending中第一个和running不重复的requestOrKey
|
||||
let index2 = -1;
|
||||
for (let i = 0; i < this.pendingRequest.length; i++) {
|
||||
let temppending = this.pendingRequest[i];
|
||||
let hasKeyEqual = false;
|
||||
for (let j = 0; j < this.runningRequest.length; j++) {
|
||||
let temprunning = this.runningRequest[j];
|
||||
if (this.requestOrKeyEqual(temppending, temprunning)) {
|
||||
hasKeyEqual = true;
|
||||
break;
|
||||
let pendingTailNode = this.pendingMaps.getTail();
|
||||
while (pendingTailNode) {
|
||||
let pendingRequest = pendingTailNode.value;
|
||||
let hasEqual = false;
|
||||
let runningTailNode = this.runningMaps.getTail();
|
||||
while (runningTailNode) {
|
||||
let runningRequest = runningTailNode.value;
|
||||
if (this.requestOrKeyEqual(pendingRequest, runningRequest)) {
|
||||
hasEqual = true;
|
||||
break
|
||||
}
|
||||
runningTailNode = runningTailNode.prev;
|
||||
}
|
||||
if (!hasKeyEqual) {
|
||||
index2 = i;
|
||||
|
||||
if (hasEqual) {
|
||||
break;
|
||||
}
|
||||
pendingTailNode = pendingTailNode.prev;
|
||||
}
|
||||
if (index2 >= 0) {
|
||||
let nextPending = this.pendingRequest.splice(index2, 1)[0];
|
||||
this.runningRequest.push(nextPending)
|
||||
|
||||
if (pendingTailNode != null && pendingTailNode.value != null) {
|
||||
let nextPending = pendingTailNode.value;
|
||||
this.runningMaps.put(nextPending.uuid, nextPending)
|
||||
this.pendingMaps.remove(nextPending.uuid)
|
||||
RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
||||
} else {
|
||||
// 不执行
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 加载下一个key的请求
|
||||
loadNextPending(request:RequestOption) {
|
||||
// 首先寻找被移除key相同的request
|
||||
let index = -1;
|
||||
for (let i = 0; i < this.pendingRequest.length; i++) {
|
||||
let temppending = this.pendingRequest[i];
|
||||
if (this.requestOrKeyEqual(request, temppending)) {
|
||||
// 如果key相同 说明目前有任务正在执行,我们记录下当前request 放入pendingRunning
|
||||
index = i;
|
||||
private loadNextPending(request: RequestOption) {
|
||||
let hasEqualRunning = false;
|
||||
let tailNode = this.pendingMaps.getTail();
|
||||
while (tailNode) {
|
||||
if (this.requestOrKeyEqual(request, tailNode.value)) {
|
||||
hasEqualRunning = true;
|
||||
break;
|
||||
}
|
||||
tailNode = tailNode.prev;
|
||||
}
|
||||
if (index >= 0) {
|
||||
this.keyEqualPendingToRun(index);
|
||||
|
||||
if (hasEqualRunning) {
|
||||
if(tailNode != null && tailNode.value != null) {
|
||||
this.keyEqualPendingToRun(tailNode.value);
|
||||
}
|
||||
} else {
|
||||
this.searchNextKeyToRun();
|
||||
}
|
||||
}
|
||||
|
||||
// 启动新线程 去磁盘取 去网络取
|
||||
loadCacheManager(request: RequestOption) {
|
||||
if (this.keyNotEmpty(request)) {
|
||||
let hasRunningRequest = false;
|
||||
for (let i = 0; i < this.runningRequest.length; i++) {
|
||||
let tempRunning = this.runningRequest[i];
|
||||
if (this.requestOrKeyEqual(request, tempRunning)) {
|
||||
private loadCacheManager(request: RequestOption) {
|
||||
if (this.isPaused) {
|
||||
// 将当前request存入pausedMaps
|
||||
this.pausedMaps.put(request.uuid, request);
|
||||
} else {
|
||||
// 正常逻辑
|
||||
if (this.keyNotEmpty(request)) {
|
||||
let hasRunningRequest = false;
|
||||
// 遍历双向链表 从尾巴到头
|
||||
let tailNode = this.runningMaps.getTail();
|
||||
while (tailNode) {
|
||||
if (this.requestOrKeyEqual(request, tailNode.value)) {
|
||||
hasRunningRequest = true;
|
||||
break;
|
||||
}
|
||||
tailNode = tailNode.prev
|
||||
}
|
||||
|
||||
// 如果requestOrKey相同 说明目前有任务正在执行,我们记录下当前request 放入pendingRunning
|
||||
hasRunningRequest = true;
|
||||
break;
|
||||
if (hasRunningRequest) {
|
||||
this.pendingMaps.put(request.uuid, request);
|
||||
|
||||
// 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) {
|
||||
this.pendingRequest.push(request);
|
||||
} else {
|
||||
this.runningRequest.push(request);
|
||||
// 不存在相同key的 任务可以并行
|
||||
RequestManager.execute(request, this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
||||
else {
|
||||
LogUtil.log("key没有生成无法进入存取!")
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogUtil.log("key没有生成无法进入存取!")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private keyNotEmpty(request: RequestOption): boolean{
|
||||
private keyNotEmpty(request: RequestOption): boolean {
|
||||
if (
|
||||
request.generateCacheKey != null && request.generateCacheKey.length > 0 &&
|
||||
request.generateDataKey != null && request.generateDataKey.length > 0 &&
|
||||
request.generateResourceKey != null && request.generateResourceKey.length > 0
|
||||
request.generateDataKey != null && request.generateDataKey.length > 0 &&
|
||||
request.generateResourceKey != null && request.generateResourceKey.length > 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private keyEqual(request1: RequestOption, request2: RequestOption): boolean{
|
||||
private keyEqual(request1: RequestOption, request2: RequestOption): boolean {
|
||||
// key 完全相等的情况
|
||||
if (
|
||||
request1.generateCacheKey == request2.generateCacheKey &&
|
||||
request1.generateResourceKey == request2.generateResourceKey &&
|
||||
request1.generateDataKey == request2.generateDataKey
|
||||
request1.generateResourceKey == request2.generateResourceKey &&
|
||||
request1.generateDataKey == request2.generateDataKey
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
@ -405,18 +491,18 @@ export class ImageKnife {
|
|||
}
|
||||
|
||||
// 非严格校验模式,如果所有key相等我们认为一定相等, 如果请求类型是string 网络请求url或者uri相等 我们也认为该请求应该只发送一个即可,后续请求会去缓存或者磁盘读取
|
||||
private requestOrKeyEqual(request1: RequestOption, request2: RequestOption): boolean{
|
||||
private requestOrKeyEqual(request1: RequestOption, request2: RequestOption): boolean {
|
||||
// key 完全相等的情况
|
||||
if (
|
||||
request1.generateCacheKey == request2.generateCacheKey &&
|
||||
request1.generateResourceKey == request2.generateResourceKey &&
|
||||
request1.generateDataKey == request2.generateDataKey
|
||||
request1.generateResourceKey == request2.generateResourceKey &&
|
||||
request1.generateDataKey == request2.generateDataKey
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果加载的是网络url或者是本地文件uri读取,那么loadSrc相同就认为是同一个请求
|
||||
if(
|
||||
if (
|
||||
typeof request1.loadSrc == 'string' && typeof request2.loadSrc == 'string' && request1.loadSrc == request2.loadSrc
|
||||
) {
|
||||
return true;
|
||||
|
@ -425,7 +511,7 @@ export class ImageKnife {
|
|||
return false;
|
||||
}
|
||||
|
||||
parseSource(request: RequestOption):void {
|
||||
private parseSource(request: RequestOption): void {
|
||||
if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') {
|
||||
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, request.loadSrc as PixelMap)
|
||||
request.loadComplete(imageKnifeData);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
import { ImageKnifeOption } from '../imageknife/ImageKnifeOption'
|
||||
import { ImageKnifeGlobal } from '../imageknife/ImageKnifeGlobal'
|
||||
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 { GIFFrame } from '../imageknife/utils/gif/GIFFrame'
|
||||
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
|
||||
|
@ -82,24 +82,30 @@ export struct ImageKnifeComponent {
|
|||
private onReadyNext?: (data:ImageKnifeData|number|undefined) => void = undefined
|
||||
private onReadyNextData:ImageKnifeData|number|undefined = undefined
|
||||
|
||||
private detachFromLayout:DetachFromLayout|undefined = undefined;
|
||||
|
||||
build() {
|
||||
Canvas(this.context)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.onAreaChange((oldValue: Area, newValue: Area) => {
|
||||
this.currentWidth = newValue.width as number
|
||||
this.currentHeight = newValue.height as number
|
||||
if (this.currentWidth <= 0 || this.currentHeight <= 0) {
|
||||
// 存在宽或者高为0,此次重回无意义,无需进行request请求
|
||||
} else {
|
||||
// 前提:宽高值均有效,值>0. 条件1:当前宽高与上一次宽高不同 条件2:当前是第一次绘制
|
||||
if ((this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) || this.firstDrawFlag) {
|
||||
this.firstDrawFlag = false;
|
||||
LogUtil.log('ImageKnifeComponent onAreaChange And Next To Execute. Canvas currentWidth =' + this.currentWidth + ' currentHeight=' + this.currentHeight)
|
||||
this.lastWidth = this.currentWidth
|
||||
this.lastHeight = this.currentHeight
|
||||
this.imageKnifeExecute()
|
||||
if(newValue != undefined && newValue.width != undefined && newValue.height != undefined) {
|
||||
this.currentWidth = newValue.width as number
|
||||
this.currentHeight = newValue.height as number
|
||||
if (this.currentWidth <= 0 || this.currentHeight <= 0) {
|
||||
// 存在宽或者高为0,此次重回无意义,无需进行request请求
|
||||
} else {
|
||||
// 前提:宽高值均有效,值>0. 条件1:当前宽高与上一次宽高不同 条件2:当前是第一次绘制
|
||||
if ((this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) || this.firstDrawFlag) {
|
||||
this.firstDrawFlag = false;
|
||||
LogUtil.log('ImageKnifeComponent onAreaChange And Next To Execute. Canvas currentWidth =' + this.currentWidth + ' currentHeight=' + this.currentHeight)
|
||||
this.lastWidth = this.currentWidth
|
||||
this.lastHeight = this.currentHeight
|
||||
this.imageKnifeExecute()
|
||||
}
|
||||
}
|
||||
}else{
|
||||
LogUtil.log('ImageKnifeComponent onAreaChange Error newValue is undefined')
|
||||
}
|
||||
})
|
||||
.onReady(() => {
|
||||
|
@ -275,6 +281,7 @@ export struct ImageKnifeComponent {
|
|||
}
|
||||
this.resetGifData()
|
||||
let request = new RequestOption();
|
||||
this.detachFromLayout = request.detachFromLayout;
|
||||
this.configNecessary(request);
|
||||
this.configCacheStrategy(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) {
|
||||
LogUtil.log('ImageKnifeComponent default drawPlaceholder start!')
|
||||
|
||||
|
||||
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
||||
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
||||
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) {
|
||||
LogUtil.log('ImageKnifeComponent default drawThumbSizeMultiplier start!')
|
||||
|
||||
|
||||
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
||||
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
||||
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) {
|
||||
LogUtil.log('ImageKnifeComponent default drawMainSource start!')
|
||||
if (data.isPixelMap()) {
|
||||
|
||||
|
||||
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
||||
let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
|
||||
LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height + 'scaleType=' + scaleType)
|
||||
|
@ -508,7 +515,7 @@ export struct ImageKnifeComponent {
|
|||
|
||||
drawRetryholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
||||
LogUtil.log('ImageKnifeComponent default drawRetryholder start!')
|
||||
|
||||
|
||||
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
||||
LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
||||
let scaleType = (typeof imageKnifeOption.retryholderScaleType == 'number') ? imageKnifeOption.retryholderScaleType : ScaleType.FIT_CENTER
|
||||
|
@ -522,7 +529,7 @@ export struct ImageKnifeComponent {
|
|||
|
||||
drawErrorholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
||||
LogUtil.log('ImageKnifeComponent default drawErrorholder start!')
|
||||
|
||||
|
||||
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
||||
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
||||
let scaleType = (typeof imageKnifeOption.errorholderSrcScaleType == 'number') ? imageKnifeOption.errorholderSrcScaleType : ScaleType.FIT_CENTER
|
||||
|
@ -584,6 +591,10 @@ export struct ImageKnifeComponent {
|
|||
|
||||
aboutToDisappear() {
|
||||
LogUtil.log('ImageKnifeComponent aboutToDisappear happened!')
|
||||
if(this.detachFromLayout != undefined){
|
||||
this.detachFromLayout.detach();
|
||||
}
|
||||
|
||||
this.resetGifData();
|
||||
}
|
||||
|
||||
|
@ -601,7 +612,7 @@ export struct ImageKnifeComponent {
|
|||
}
|
||||
|
||||
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 {
|
||||
if (drawLifeCycle && (drawLifeCycle as Record<string,Function>)[methodName]) {
|
||||
return (drawLifeCycle as Record<string,Function>)[methodName](context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
|
||||
|
@ -677,7 +688,7 @@ export struct ImageKnifeComponent {
|
|||
// 理论上该帧在屏幕上保留的时间
|
||||
let stayTime:number= 0
|
||||
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) {
|
||||
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) {
|
||||
if(frames == undefined){
|
||||
return
|
||||
}
|
||||
if(frames == undefined){
|
||||
return
|
||||
}
|
||||
// get current frame
|
||||
let frame = frames[index];
|
||||
if (!frame || !context) {
|
||||
|
@ -848,9 +859,9 @@ export class ScaleTypeHelper {
|
|||
let dy:number = (compHeight - imageHeight * minScale) / (minScale * 1.0);
|
||||
let dw:number = imageWidth;
|
||||
let dh:number = imageHeight;
|
||||
if(source!= undefined) {
|
||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||
}
|
||||
if(source!= undefined) {
|
||||
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) {
|
||||
|
@ -859,9 +870,9 @@ export class ScaleTypeHelper {
|
|||
let dy:number = (compHeight - imageHeight * minScale) / (minScale * 2.0);
|
||||
let dw:number = imageWidth;
|
||||
let dh:number = imageHeight;
|
||||
if(source!= undefined) {
|
||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||
}
|
||||
if(source!= undefined) {
|
||||
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) {
|
||||
|
@ -869,9 +880,9 @@ export class ScaleTypeHelper {
|
|||
let dy:number = (compHeight - imageHeight) / 2.0;
|
||||
let dw:number = imageWidth;
|
||||
let dh:number = imageHeight;
|
||||
if(source!= undefined) {
|
||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||
}
|
||||
if(source!= undefined) {
|
||||
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) {
|
||||
|
@ -880,9 +891,9 @@ export class ScaleTypeHelper {
|
|||
let dy:number = (compHeight - imageHeight * maxScale) / (maxScale * 2.0);
|
||||
let dw:number = imageWidth;
|
||||
let dh:number = imageHeight;
|
||||
if(source!= undefined) {
|
||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||
}
|
||||
if(source!= undefined) {
|
||||
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) {
|
||||
|
@ -891,9 +902,9 @@ export class ScaleTypeHelper {
|
|||
let dy:number = 0;
|
||||
let dw:number = imageWidth;
|
||||
let dh:number = imageHeight;
|
||||
if(source!= undefined) {
|
||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||
}
|
||||
if(source!= undefined) {
|
||||
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) {
|
||||
|
@ -912,7 +923,7 @@ export class ScaleTypeHelper {
|
|||
if(source!= undefined) {
|
||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -84,4 +84,45 @@ export class ImageKnifeData {
|
|||
isResource(): boolean {
|
||||
return ImageKnifeType.RESOURCE == this.imageKnifeType;
|
||||
}
|
||||
|
||||
release(){
|
||||
if(this.isPixelMap()){
|
||||
if(this.drawPixelMap != undefined && this.drawPixelMap.imagePixelMap != undefined){
|
||||
this.drawPixelMap.imagePixelMap.release()
|
||||
.then(()=>{
|
||||
if(this.drawPixelMap != undefined && this.drawPixelMap.imagePixelMap !=undefined){
|
||||
this.drawPixelMap.imagePixelMap = undefined;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
if(this.isGIFFrame()){
|
||||
if(this.drawGIFFrame != undefined){
|
||||
let gifFrames = this.drawGIFFrame.imageGIFFrames;
|
||||
if(gifFrames != undefined){
|
||||
for (let i = 0; i < gifFrames.length; i++) {
|
||||
let tempFrame = gifFrames[i];
|
||||
if(tempFrame.drawPixelMap != undefined){
|
||||
tempFrame.drawPixelMap.release()
|
||||
}
|
||||
}
|
||||
this.drawGIFFrame.imageGIFFrames = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(this.isString()){
|
||||
if(this.drawString != undefined && this.drawString.imageString!=undefined){
|
||||
this.drawString.imageString = undefined
|
||||
}
|
||||
}
|
||||
|
||||
if(this.isResource()){
|
||||
if(this.drawResource != undefined && this.drawResource.imageResource != undefined){
|
||||
this.drawResource.imageResource = undefined
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -54,7 +54,12 @@ export interface Size {
|
|||
width: number,
|
||||
height: number
|
||||
}
|
||||
export interface DetachFromLayout{
|
||||
detach:()=>void
|
||||
}
|
||||
|
||||
export class RequestOption {
|
||||
uuid:string ='' // 唯一标识
|
||||
loadSrc: string | PixelMap | Resource = '';
|
||||
strategy: DiskStrategy = new AUTOMATIC();
|
||||
dontAnimateFlag = false;
|
||||
|
@ -117,11 +122,30 @@ export class RequestOption {
|
|||
// 缩略图展示
|
||||
loadThumbnailReady = false;
|
||||
|
||||
|
||||
detachFromLayout:DetachFromLayout = {
|
||||
detach: ()=>{
|
||||
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||
if(imageKnife != undefined) {
|
||||
imageKnife.remove(this.uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
// 初始化全局监听
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
load(src: string | PixelMap | Resource) {
|
||||
this.loadSrc = src;
|
||||
return this;
|
||||
|
@ -489,15 +511,15 @@ export class RequestOption {
|
|||
// 非落盘情况,直接进行寻找下一个加载
|
||||
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||
if(imageKnife != undefined) {
|
||||
imageKnife.removeRunning(this);
|
||||
imageKnife.removeRunning(this);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载成功之后
|
||||
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||
if(imageKnife != undefined) {
|
||||
imageKnife.removeRunning(this);
|
||||
}
|
||||
// // 加载成功之后
|
||||
// let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||
// if(imageKnife != undefined) {
|
||||
// imageKnife.removeRunning(this);
|
||||
// }
|
||||
}
|
||||
|
||||
// 图片文件落盘之后会自动去寻找下一个数据加载
|
||||
|
|
|
@ -98,10 +98,11 @@ export class Engine {
|
|||
}
|
||||
imageResource.createPixelMap(options)
|
||||
.then(bitmap => {
|
||||
|
||||
imageResource.release()
|
||||
})
|
||||
.catch((error: BusinessError) => {
|
||||
this.mCompressListener.onError("ptah createPixelMap fail,because error:" + JSON.stringify(error as BusinessError))
|
||||
imageResource.release()
|
||||
})
|
||||
|
||||
|
||||
|
@ -173,11 +174,13 @@ export class Engine {
|
|||
if (this.mCompressListener) {
|
||||
this.mCompressListener.onSuccess(bitmap, path);
|
||||
}
|
||||
imageRes.release()
|
||||
})
|
||||
.catch((error:BusinessError) => {
|
||||
if (this.mCompressListener) {
|
||||
this.mCompressListener.onError("buffer generated pixelMap fail,because error:" + JSON.stringify(error as BusinessError))
|
||||
}
|
||||
imageRes.release()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,10 @@ export class RecourseProvider extends CompressAdapter {
|
|||
.then(data => {
|
||||
let buffer = this.uint8ArrayToBuffer(data);
|
||||
let fileTypeUtil = new FileTypeUtil()
|
||||
this._mPixelMapHeader = fileTypeUtil.getFileType(buffer);
|
||||
let fileType = fileTypeUtil.getFileType(buffer);
|
||||
if(fileType != null) {
|
||||
this._mPixelMapHeader = fileType;
|
||||
}
|
||||
callback.compressDataListener(buffer);
|
||||
})
|
||||
.catch((err: BusinessError) => {
|
||||
|
|
|
@ -66,9 +66,11 @@ export namespace Crop {
|
|||
} else {
|
||||
func?.cropCallback("", data);
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
func?.cropCallback(e, null);
|
||||
imageSource.release()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -893,6 +893,7 @@ export class Options {
|
|||
if (readyCrop) {
|
||||
readyCrop();
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
|
||||
});
|
||||
|
|
|
@ -53,23 +53,28 @@ export class ErrorHolderManager<T> {
|
|||
if (typeof res.id != 'undefined' && typeof res.id != 'undefined') {
|
||||
let resourceFetch = new ParseResClient();
|
||||
let suc = (arraybuffer:ArrayBuffer) => {
|
||||
let fileTypeUtil:FileTypeUtil = new FileTypeUtil();
|
||||
let typeValue:string = fileTypeUtil.getFileType(arraybuffer);
|
||||
switch (typeValue) {
|
||||
case SupportFormat.svg:
|
||||
this.svgProcess(onComplete, onError, arraybuffer, typeValue)
|
||||
break;
|
||||
case SupportFormat.jpg:
|
||||
case SupportFormat.png:
|
||||
case SupportFormat.bmp:
|
||||
case SupportFormat.gif:
|
||||
case SupportFormat.tiff:
|
||||
case SupportFormat.webp:
|
||||
this.mediaImageProcess(onComplete, onError, arraybuffer, typeValue)
|
||||
break;
|
||||
default:
|
||||
onError("ErrorHolderManager 文件类型不支持")
|
||||
break;
|
||||
let fileTypeUtil: FileTypeUtil = new FileTypeUtil();
|
||||
|
||||
let typeValue: string | null = fileTypeUtil.getFileType(arraybuffer);
|
||||
if (typeValue != null) {
|
||||
switch (typeValue) {
|
||||
case SupportFormat.svg:
|
||||
this.svgProcess(onComplete, onError, arraybuffer, typeValue)
|
||||
break;
|
||||
case SupportFormat.jpg:
|
||||
case SupportFormat.png:
|
||||
case SupportFormat.bmp:
|
||||
case SupportFormat.gif:
|
||||
case SupportFormat.tiff:
|
||||
case SupportFormat.webp:
|
||||
this.mediaImageProcess(onComplete, onError, arraybuffer, typeValue)
|
||||
break;
|
||||
default:
|
||||
onError("ErrorHolderManager 文件类型不支持")
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
onError("ErrorHolderManager 文件类型为null,请检查数据源arraybuffer")
|
||||
}
|
||||
}
|
||||
resourceFetch.loadResource(res, suc, onError)
|
||||
|
|
|
@ -15,16 +15,19 @@
|
|||
|
||||
import { IDataFetch } from '../networkmanage/IDataFetch'
|
||||
import { RequestOption } from '../RequestOption'
|
||||
import { NetworkDownloadClient } from './NetworkDownloadClient'
|
||||
|
||||
import { HttpDownloadClient } from './HttpDownloadClient'
|
||||
import { LoadLocalFileClient } from './LoadLocalFileClient'
|
||||
import { LoadDataShareFileClient } from './LoadDataShareFileClient'
|
||||
import loadRequest from '@ohos.request';
|
||||
import { ImageKnifeGlobal } from '../ImageKnifeGlobal'
|
||||
import common from '@ohos.app.ability.common'
|
||||
import { NetworkDownloadClient } from './NetworkDownloadClient'
|
||||
|
||||
// 数据加载器
|
||||
export class DownloadClient implements IDataFetch {
|
||||
private networkClient = new NetworkDownloadClient();
|
||||
private networkDownloadClient = new NetworkDownloadClient();
|
||||
private httpDownloadClient = new HttpDownloadClient();
|
||||
private localFileClient = new LoadLocalFileClient();
|
||||
private dataShareFileClient = new LoadDataShareFileClient();
|
||||
|
||||
|
@ -41,7 +44,7 @@ export class DownloadClient implements IDataFetch {
|
|||
this.dataShareFileClient.loadData(request, onCompleteFunction, onErrorFunction)
|
||||
} else {
|
||||
// 网络下载
|
||||
this.networkClient.loadData(request, onCompleteFunction, onErrorFunction)
|
||||
this.networkDownloadClient.loadData(request, onCompleteFunction, onErrorFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -292,6 +292,8 @@ export class RequestManager {
|
|||
// 处理磁盘加载 网络加载
|
||||
this.runWrapped(this.options, this.mRunReason, onComplete, onError)
|
||||
} else {
|
||||
// 需要清理状态
|
||||
cache.waitSaveDisk = false;
|
||||
onComplete(cache);
|
||||
return
|
||||
|
||||
|
@ -431,7 +433,12 @@ export class RequestManager {
|
|||
// 步骤二: 文件名保存一份全局
|
||||
// 步骤三:查看文件是否支持 非支持类型直接返回
|
||||
let fileTypeUtil = new FileTypeUtil();
|
||||
let filetype = fileTypeUtil.getFileType(source);
|
||||
let filetype:string|null = fileTypeUtil.getFileType(source);
|
||||
if(filetype == null){
|
||||
onError("下载文件解析后类型为null,请检查数据源!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fileTypeUtil.isImage(source)) {
|
||||
onError("暂不支持 下载文件类型!类型=" + filetype);
|
||||
return;
|
||||
|
@ -471,11 +478,15 @@ export class RequestManager {
|
|||
if (this.options.transformations[0]) {
|
||||
// thumbnail 缩略图部分
|
||||
if (this.options.thumbSizeMultiplier) {
|
||||
this.thumbnailProcess(source, filetype, onComplete, onError);
|
||||
if(filetype != null) {
|
||||
this.thumbnailProcess(source, filetype, onComplete, onError);
|
||||
}
|
||||
} else {
|
||||
this.options.transformations[0].transform(source, this.options, {asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => {
|
||||
if (pixelMap) {
|
||||
this.saveCacheAndDisk(pixelMap, filetype, onComplete, source);
|
||||
if(filetype != null) {
|
||||
this.saveCacheAndDisk(pixelMap, filetype, onComplete, source);
|
||||
}
|
||||
} else {
|
||||
onError(error);
|
||||
}
|
||||
|
@ -493,13 +504,17 @@ export class RequestManager {
|
|||
this.mParseImageUtil.parseImageThumbnail(this.options.thumbSizeMultiplier, source, thumbSuccess, thumbError);
|
||||
setTimeout(() => {
|
||||
let success = (value: PixelMap) => {
|
||||
this.saveCacheAndDisk(value, filetype, onComplete, source);
|
||||
if(filetype != null) {
|
||||
this.saveCacheAndDisk(value, filetype, onComplete, source);
|
||||
}
|
||||
}
|
||||
this.mParseImageUtil.parseImage(source, success, onError)
|
||||
}, this.options.thumbDelayTime)
|
||||
} else {
|
||||
let success = (value: PixelMap) => {
|
||||
this.saveCacheAndDisk(value, filetype, onComplete, source);
|
||||
if(filetype != null) {
|
||||
this.saveCacheAndDisk(value, filetype, onComplete, source);
|
||||
}
|
||||
}
|
||||
this.mParseImageUtil.parseImage(source, success, onError)
|
||||
}
|
||||
|
|
|
@ -74,11 +74,13 @@ export class BlurTransformation implements BaseTransform<PixelMap> {
|
|||
} else {
|
||||
fastBlur.blur(data, this._mRadius, true, func);
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
|
||||
imageSource.release()
|
||||
func?.asyncTransform(e, null);
|
||||
})
|
||||
})
|
||||
}})
|
||||
}
|
||||
}
|
|
@ -76,7 +76,7 @@ export class BrightnessFilterTransformation implements BaseTransform<PixelMap> {
|
|||
}
|
||||
}
|
||||
let data = await imageSource.createPixelMap(options);
|
||||
|
||||
imageSource.release()
|
||||
let bufferData = new ArrayBuffer(data.getPixelBytesNumber());
|
||||
await data.readPixelsToBuffer(bufferData);
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ export class ContrastFilterTransformation implements BaseTransform<PixelMap> {
|
|||
}
|
||||
|
||||
let data = await imageSource.createPixelMap(options);
|
||||
|
||||
imageSource.release()
|
||||
let bufferData = new ArrayBuffer(data.getPixelBytesNumber());
|
||||
await data.readPixelsToBuffer(bufferData);
|
||||
|
||||
|
|
|
@ -75,12 +75,14 @@ export class CropCircleTransformation implements BaseTransform<PixelMap> {
|
|||
imageSource.createPixelMap(options)
|
||||
.then((p:PixelMap) => {
|
||||
this.transformCircle(p, func);
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
LogUtil.log(Constants.PROJECT_TAG + CropCircleTransformation.TAG + " transform e:" + e);
|
||||
if (func!=undefined) {
|
||||
func?.asyncTransform(Constants.PROJECT_TAG + CropCircleTransformation.TAG + "e" + e, null);
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -93,12 +93,14 @@ export class CropCircleWithBorderTransformation implements BaseTransform<PixelMa
|
|||
imageSource.createPixelMap(options)
|
||||
.then((pixelMap:PixelMap) => {
|
||||
this.transformPixelMap(pixelMap, outWith, outHeight, func);
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
LogUtil.log(Constants.PROJECT_TAG + ";CropCircleWithBorderTransformation e:" + e);
|
||||
if (func!=undefined) {
|
||||
func?.asyncTransform(Constants.PROJECT_TAG + ";CropCircleWithBorderTransformation e:" + e, null);
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -73,11 +73,13 @@ export class CropSquareTransformation implements BaseTransform<PixelMap> {
|
|||
if (func != undefined) {
|
||||
func?.asyncTransform("", data);
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
if (func != undefined) {
|
||||
func?.asyncTransform(Constants.PROJECT_TAG + ";CropSquareTransformation e:" + e, null);
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
})
|
||||
.catch((error:BusinessError) => {
|
||||
|
|
|
@ -83,9 +83,11 @@ export class CropTransformation implements BaseTransform<PixelMap> {
|
|||
imageSource.createPixelMap(options)
|
||||
.then((data:PixelMap) => {
|
||||
func?.asyncTransform("", data);
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
|
||||
imageSource.release()
|
||||
func?.asyncTransform(e, null);
|
||||
})
|
||||
}})
|
||||
|
|
|
@ -68,7 +68,7 @@ export class GrayscaleTransformation implements BaseTransform<PixelMap> {
|
|||
}
|
||||
}
|
||||
let data:PixelMap= await imageSource.createPixelMap(options);
|
||||
|
||||
imageSource.release()
|
||||
let bufferData = new ArrayBuffer(data.getPixelBytesNumber());
|
||||
let bufferNewData = new ArrayBuffer(data.getPixelBytesNumber());
|
||||
await data.readPixelsToBuffer(bufferData);
|
||||
|
|
|
@ -77,7 +77,7 @@ export class InvertFilterTransformation implements BaseTransform<PixelMap> {
|
|||
}
|
||||
|
||||
let data:PixelMap = await imageSource.createPixelMap(options);
|
||||
|
||||
imageSource.release()
|
||||
let bufferData = new ArrayBuffer(data.getPixelBytesNumber());
|
||||
await data.readPixelsToBuffer(bufferData);
|
||||
|
||||
|
|
|
@ -79,10 +79,12 @@ export class KuwaharaFilterTransform implements BaseTransform<PixelMap> {
|
|||
imageSource.createPixelMap(options)
|
||||
.then((data) => {
|
||||
this.kuwahara(data, targetWidth, targetHeight, func);
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
|
||||
func?.asyncTransform(e, null);
|
||||
imageSource.release()
|
||||
})
|
||||
}})
|
||||
}
|
||||
|
|
|
@ -79,9 +79,11 @@ export class MaskTransformation implements BaseTransform<PixelMap> {
|
|||
imageSource.createPixelMap(options)
|
||||
.then(data => {
|
||||
this.openInternal(data, targetWidth, targetHeight, func)
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError )=> {
|
||||
func?.asyncTransform(e, null);
|
||||
imageSource.release()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -80,9 +80,11 @@ export class PixelationFilterTransformation implements BaseTransform<PixelMap> {
|
|||
} else {
|
||||
pixelUtils.pixel(data, this._mPixel, func);
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
|
||||
imageSource.release();
|
||||
func?.asyncTransform(e, null);
|
||||
})
|
||||
}})
|
||||
|
|
|
@ -70,9 +70,11 @@ export class RotateImageTransformation implements BaseTransform<PixelMap> {
|
|||
imageSource.createPixelMap(options)
|
||||
.then((data) => {
|
||||
func?.asyncTransform("", data);
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
|
||||
imageSource.release()
|
||||
func?.asyncTransform(e, null);
|
||||
})
|
||||
}})
|
||||
|
|
|
@ -113,8 +113,10 @@ export class RoundedCornersTransformation implements BaseTransform<PixelMap> {
|
|||
if (func != undefined && this.mTransform_pixelMap != undefined) {
|
||||
func?.asyncTransform("", this.mTransform_pixelMap);
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((error:BusinessError) => {
|
||||
imageSource.release()
|
||||
LogUtil.log(Constants.PROJECT_TAG + "RoundedCornersTransformation error:" + error);
|
||||
});
|
||||
})
|
||||
|
|
|
@ -73,7 +73,7 @@ export class SepiaFilterTransformation implements BaseTransform<PixelMap> {
|
|||
}
|
||||
}
|
||||
let data:PixelMap = await imageSource.createPixelMap(options);
|
||||
|
||||
imageSource.release();
|
||||
let bufferData = new ArrayBuffer(data.getPixelBytesNumber());
|
||||
await data.readPixelsToBuffer(bufferData);
|
||||
|
||||
|
|
|
@ -66,9 +66,11 @@ export class SketchFilterTransformation implements BaseTransform<PixelMap> {
|
|||
} else {
|
||||
CalculatePixelUtils.sketch(data, func);
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
func?.asyncTransform(e, null);
|
||||
imageSource.release()
|
||||
})
|
||||
}})
|
||||
}
|
||||
|
|
|
@ -81,8 +81,10 @@ export class SwirlFilterTransformation implements BaseTransform<PixelMap> {
|
|||
imageSource.createPixelMap(options)
|
||||
.then((data) => {
|
||||
this.swirl(data, this.radius, request, func);
|
||||
imageSource.release();
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
imageSource.release();
|
||||
func?.asyncTransform(e, null);
|
||||
})
|
||||
}})
|
||||
|
|
|
@ -83,9 +83,11 @@ export class ToonFilterTransform implements BaseTransform<PixelMap> {
|
|||
}
|
||||
imageSource.createPixelMap(options)
|
||||
.then((data) => {
|
||||
imageSource.release()
|
||||
this.toon(data, targetWidth, targetHeight, func);
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
imageSource.release()
|
||||
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
|
||||
func?.asyncTransform(e, null);
|
||||
})
|
||||
|
|
|
@ -63,7 +63,9 @@ export class TransformUtils {
|
|||
editable: true,
|
||||
rotate: degreesToRotate
|
||||
}
|
||||
return imageSource.createPixelMap(options);
|
||||
let promise:Promise<PixelMap> = imageSource.createPixelMap(options);
|
||||
imageSource.release()
|
||||
return promise;
|
||||
}
|
||||
|
||||
static centerInside(buf: ArrayBuffer, outWidth: number, outHeihgt: number,
|
||||
|
@ -74,7 +76,9 @@ export class TransformUtils {
|
|||
let pw = p.size.width;
|
||||
let ph = p.size.height;
|
||||
if (pw <= outWidth && ph <= outHeihgt) {
|
||||
callback?.asyncTransform('', imageSource.createPixelMap());
|
||||
let promise:Promise<PixelMap> = imageSource.createPixelMap()
|
||||
imageSource.release()
|
||||
callback?.asyncTransform('', promise);
|
||||
} else {
|
||||
TransformUtils.fitCenter(buf, outWidth, outHeihgt, callback);
|
||||
}
|
||||
|
@ -112,7 +116,9 @@ export class TransformUtils {
|
|||
desiredSize: { width: targetWidth, height: targetHeight }
|
||||
}
|
||||
if (callback) {
|
||||
callback.asyncTransform('', imageSource.createPixelMap(options));
|
||||
let promise:Promise<PixelMap> = imageSource.createPixelMap(options);
|
||||
imageSource.release();
|
||||
callback.asyncTransform('', promise);
|
||||
}
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
|
|
|
@ -88,8 +88,10 @@ export class VignetteFilterTransform implements BaseTransform<PixelMap> {
|
|||
imageSource.createPixelMap(options)
|
||||
.then((data) => {
|
||||
this.vignette(data, targetWidth, targetHeight, func);
|
||||
imageSource.release();
|
||||
})
|
||||
.catch((e:BusinessError) => {
|
||||
imageSource.release()
|
||||
LogUtil.log(Constants.PROJECT_TAG + ";error:" + e);
|
||||
func?.asyncTransform(e, null);
|
||||
})
|
||||
|
|
|
@ -12,143 +12,90 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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 {
|
||||
private map:Map<string,string> = new Map<string,string>();
|
||||
private READ_MIN_LENGTH:number = 0;
|
||||
private SUPPORT_FORMATS = [
|
||||
PhotoFormat.jpg,
|
||||
PhotoFormat.png,
|
||||
PhotoFormat.tiff,
|
||||
PhotoFormat.bmp,
|
||||
PhotoFormat.webp,
|
||||
PhotoFormat.svg,
|
||||
PhotoFormat.gif
|
||||
]
|
||||
private fileSignatureMap: Record<string, Array<Uint8Array>> = {
|
||||
// 添加文件类型和对应的文件头部特征
|
||||
'jpg': [new Uint8Array([0xFF, 0xD8])],
|
||||
'png': [new Uint8Array([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])],
|
||||
'gif': [new Uint8Array([0x47, 0x49, 0x46, 0x38, 0x39, 0x61])],
|
||||
'bmp': [new Uint8Array([0x42, 0x4D])],
|
||||
'svg': [new Uint8Array([0x3C, 0x3F, 0x78, 0x6D, 0x6C])],
|
||||
'webp': [new Uint8Array([0x52, 0x49, 0x46, 0x46])],
|
||||
'tiff': [new Uint8Array([0x49, 0x20, 0x49]), new Uint8Array([0x49, 0x49, 0x2A, 0x00]), new Uint8Array([0x4D, 0x4D, 0x00, 0x2A]), new Uint8Array([0x4D, 0x4D, 0x00, 0x2B])],
|
||||
// 添加更多的文件类型和特征
|
||||
};
|
||||
|
||||
|
||||
constructor() {
|
||||
this.initImageType();
|
||||
|
||||
}
|
||||
|
||||
private getDataViewAt(dataView:DataView,index:number): string{
|
||||
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){
|
||||
isImage(arraybuffer: ArrayBuffer) {
|
||||
let value = this.getFileType(arraybuffer);
|
||||
if(
|
||||
if (
|
||||
value == SupportFormat.jpg ||
|
||||
value == SupportFormat.png ||
|
||||
value == SupportFormat.tiff ||
|
||||
value == SupportFormat.webp ||
|
||||
value == SupportFormat.bmp ||
|
||||
value == SupportFormat.gif ||
|
||||
value == SupportFormat.svg
|
||||
){
|
||||
value == SupportFormat.png ||
|
||||
value == SupportFormat.tiff ||
|
||||
value == SupportFormat.webp ||
|
||||
value == SupportFormat.bmp ||
|
||||
value == SupportFormat.gif ||
|
||||
value == SupportFormat.svg
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
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 {
|
||||
jpg = 'jpg,0,FFD8',
|
||||
png = 'png,0,89504E470D0A1A0A',
|
||||
|
|
|
@ -48,6 +48,7 @@ export class ParseImageUtil implements IParseImage<PixelMap> {
|
|||
} else {
|
||||
onCompleteFunction(pixelmap);
|
||||
}
|
||||
imageSource.release()
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,6 @@
|
|||
"name": "imageknife",
|
||||
"description": "example description",
|
||||
"repository": {},
|
||||
"version": "2.1.1-rc.0",
|
||||
"version": "2.1.1-rc.1",
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue