From 4a8794bbfd59cde1f9e220f00f935617295cddff Mon Sep 17 00:00:00 2001 From: sijainguo Date: Sat, 27 Apr 2024 17:36:31 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20@Sendable=20=E8=A3=85?= =?UTF-8?q?=E9=A5=B0=E5=99=A8=E4=BF=AE=E9=A5=B0=E7=B1=BB=EF=BC=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Downsampling/BaseDownsampling.ets | 3 +++ .../Downsampling/DownsampleStartegy.ets | 26 +++++++++++++++++-- .../imageknife/Downsampling/Downsampler.ets | 19 +++++++------- .../ets/components/imageknife/ImageKnife.ets | 3 ++- .../imageknife/ImageKnifeOption.ets | 5 ++++ .../components/imageknife/SendableData.ets | 12 ++++++++- .../imageknife/utils/ParseImageUtil.ets | 20 ++++++++------ 7 files changed, 67 insertions(+), 21 deletions(-) diff --git a/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets b/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets index fa7add3..9ade775 100644 --- a/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets +++ b/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets @@ -1,4 +1,7 @@ +import {lang}from '@kit.ArkTs'; +type ISendable=lang.ISendable; export interface BaseDownsampling{ + getName():string getScaleFactor(sourceWidth:number, sourceHeight:number, requestWidth:number, requestHeight:number):number diff --git a/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets b/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets index 01fe132..3d6583d 100644 --- a/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets +++ b/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets @@ -1,7 +1,8 @@ import { BaseDownsampling } from './BaseDownsampling' - +@Sendable export class CenterInside{ + getName() { return "CenterInside"} getScaleFactor(sourceWidth:number, sourceHeight:number,requestWidth:number, requestHeight:number):number { return Math.min(1,FitCenter.getScaleFactor(sourceWidth, sourceHeight, requestWidth, requestHeight)) @@ -15,7 +16,11 @@ export class CenterInside{ } /*不进行下采样*/ +@Sendable export class DownsampleNone implements BaseDownsampling{ + getName(){ + return "DownsampleNone" + } getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { return 1 } @@ -26,7 +31,11 @@ export class DownsampleNone implements BaseDownsampling{ } /* 下采样使得图像的组大尺寸在给定的尺寸的1/2之间*/ +@Sendable export class AtMost implements BaseDownsampling{ + getName(){ + return "AtMost" + } getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth:number,requestHeight: number): number { let maxIntegerFactor=Math.ceil(Math.max(sourceHeight/requestHeight,sourceWidth/requestWidth)); let lesserOrEqualSampleSize = Math.max(1,highestOneBit(maxIntegerFactor)) @@ -37,7 +46,12 @@ export class DownsampleNone implements BaseDownsampling{ return SampleSizeRounding.MEMORY } } + +@Sendable export class Atleast implements BaseDownsampling{ + getName(){ + return "Atleast" + } getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { let minIntegerFactor=Math.floor(Math.min(sourceHeight/requestHeight,sourceWidth/requestWidth)) @@ -47,7 +61,12 @@ export class Atleast implements BaseDownsampling{ return SampleSizeRounding.QUALITY } } + +@Sendable export class CenterOutside implements BaseDownsampling{ + getName(){ + return "CenterOutside" + } getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { let widthPercentage =requestWidth/sourceWidth let heightPercentage =requestHeight/sourceHeight @@ -60,8 +79,11 @@ export class CenterOutside implements BaseDownsampling{ } - +@Sendable export class FitCenter{ + getName(){ + return "FitCenter" + } public static getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { let widthPercentage =requestWidth/sourceWidth let heightPercentage =requestHeight/sourceHeight diff --git a/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets b/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets index 2bf9a9e..da8ac1d 100644 --- a/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets +++ b/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets @@ -1,8 +1,9 @@ /* * asdfasdfasdfasdfasdf*/ -import { CenterOutside, FitCenter, SampleSizeRounding } from './Downsamplestrategy'; -import { FileTypeUtil } from '../../utits/Fitetypeutit'; -import { RequestOption } from '../../Requestoption'; +import { RequestOption } from '../RequestOption'; +import { FileTypeUtil } from '../utils/FileTypeUtil'; +import { CenterOutside, FitCenter, SampleSizeRounding } from './DownsampleStartegy'; + let TAG = 'downsampling' export class Downsampler { calculateScaling( @@ -26,7 +27,7 @@ export class Downsampler { } else { console.log('宽高都不存在') } - if (sourceWidth <= 0 || sourceleight <= 0) return; + if (sourceWidth <= 0 || sourceHeight <= 0) return; let orientedSourceWidth = sourceWidth; let orientedSourceHeight = sourceHeight; if (this.isRotationRequired(90)) { @@ -52,7 +53,7 @@ export class Downsampler { let powerOfTwoSampleSize: number; powerOfTwoSampleSize = Math.max(1, this.highestOneBit(scaleFactor)); if (fileType == "JPEG") { - let nativeScaling = Hath.min(powerOfTwoSampleSize, 8); + let nativeScaling = Math.min(powerOfTwoSampleSize, 8); powerOfTwoWidth = Math.ceil(orientedSourceWidth / nativeScaling); powerOfTwoHeight = Math.ceil(orientedSourceHeight / nativeScaling); let secondaryScaling = Math.floor(powerOfTwoSampleSize / 8); @@ -62,11 +63,11 @@ export class Downsampler { } } else if (fileType == "PNG") { powerOfTwoWidth = Math.floor(orientedSourceWidth / powerOfTwoSampleSize); - PowerOfTwoHeight = Math.floor(orientedSourceHeight / powerOfTwoSamplesize); + powerOfTwoHeight = Math.floor(orientedSourceHeight / powerOfTwoSampleSize); console.log('执行了没', powerOfTwoHeight, powerOfTwoWidth) } else if (fileType == "WEBP") { powerOfTwoWidth = Math.round(orientedSourceWidth / powerOfTwoSampleSize); - poWerOfTwoHeight = Math.round(orientedSourceHeight / powerOfTwoSampleSize); + powerOfTwoHeight = Math.round(orientedSourceHeight / powerOfTwoSampleSize); } else if ( orientedSourceWidth % powerOfTwoSampleSize != 0 || orientedSourceHeight % powerOfTwoSampleSize != 0) { @@ -91,7 +92,7 @@ export class Downsampler { // options.inDensity = options.inTargetDensity =0; // } //} - let a: ESObject = { "targetWidth": power0fTwoWidth, "targetHeight": powerOfTuoHeight } + let a: ESObject = { "targetWidth": powerOfTwoWidth, "targetHeight": powerOfTwoHeight } return a } //decodeStream(imageReader:ImageReader, options: ESObject, callbacks: DecodeCallbacks, bitmapPool: ESObject) { @@ -108,7 +109,7 @@ export class Downsampler { // // } - highest0neBit(i: number): number{ + highestOneBit(i: number): number{ i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); diff --git a/library/src/main/ets/components/imageknife/ImageKnife.ets b/library/src/main/ets/components/imageknife/ImageKnife.ets index 3e784a1..8108f8b 100644 --- a/library/src/main/ets/components/imageknife/ImageKnife.ets +++ b/library/src/main/ets/components/imageknife/ImageKnife.ets @@ -563,6 +563,7 @@ export class ImageKnife { data.setDiskMemoryCachePath(this.diskMemoryCache.getPath()) data.setPlaceHolderRegisterCacheKey(request.placeholderRegisterCacheKey); data.setPlaceHolderRegisterMemoryCacheKey(request.placeholderRegisterMemoryCacheKey); + data.setDownsampType(request.downsampType) return data; } @@ -811,7 +812,7 @@ async function taskExecute(sendData:SendableData,taskData:TaskParams): Promise

{ parseImage(imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void,request?:RequestOption) { @@ -37,14 +38,17 @@ export class ParseImageUtil implements IParseImage { editable: true, desiredSize: defaultSize }; - const b = new Downsamper(imageinfo, hValue, wValue, requ,request) - let options: image.DecodingOptions = { - editable: true, - desiredSize: { - height: b.targetHeight, - width: b.targetWidth - } - }; + if(request.downsampType.getName()!=='DownsampleNone'){ + const b:ESObject = new Downsampler().calculateScaling(imageinfo, hValue, wValue,request) + opts= { + editable: true, + desiredSize: { + height: b.targetHeight, + width: b.targetWidth + } + }; + } + imageSource.createPixelMap(opts).then((pixelMap: image.PixelMap) => { From 2a01e5ea4dee9565496dbfbc74e96976023f09b3 Mon Sep 17 00:00:00 2001 From: sijainguo Date: Mon, 29 Apr 2024 11:23:35 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E9=99=8D=E9=87=87=E6=A0=B7=E9=80=82?= =?UTF-8?q?=E9=85=8DSVG=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../imageknife/Downsampling/Downsampler.ets | 210 +++++++++--------- .../imageknife/utils/ParseImageUtil.ets | 7 +- .../imageknife/utils/svg/SVGParseImpl.ets | 23 ++ 3 files changed, 139 insertions(+), 101 deletions(-) diff --git a/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets b/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets index da8ac1d..b2cfa5f 100644 --- a/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets +++ b/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets @@ -5,13 +5,14 @@ import { FileTypeUtil } from '../utils/FileTypeUtil'; import { CenterOutside, FitCenter, SampleSizeRounding } from './DownsampleStartegy'; let TAG = 'downsampling' + export class Downsampler { calculateScaling( imageType: ArrayBuffer, - sourceHeight:number, - sourceWidth:number, + sourceHeight: number, + sourceWidth: number, request?: RequestOption - ):ESObject { + ): ESObject { const fileType: string | null = new FileTypeUtil().getFileType(imageType) let powerOfTwoWidth: number | null = null; let powerOfTwoHeight: number | null = null; @@ -39,103 +40,112 @@ export class Downsampler { if (exactScaleFactor <= 0) { throw new Error("Cannot round with exactScaleFactor"); } - console.log('exactScaleFactor', exactScaleFactor) - /*安卓的模式*/ - let rounding: SampleSizeRounding = CenterOutside.getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) - if (rounding == null) { - throw new Error("Cannot round with null rounding"); - } - let outWidth: number = this.round(exactScaleFactor * orientedSourceWidth); - let outHeight: number = this.round(exactScaleFactor * orientedSourceHeight); - let widthScaleFactor = Math.floor(orientedSourceWidth / outWidth); - let heightScaleFactor = Math.floor(orientedSourceHeight / outHeight); - let scaleFactor = rounding == SampleSizeRounding.MEMORY ? Math.max(widthScaleFactor, heightScaleFactor) : Math.min(widthScaleFactor, heightScaleFactor) - let powerOfTwoSampleSize: number; - powerOfTwoSampleSize = Math.max(1, this.highestOneBit(scaleFactor)); - if (fileType == "JPEG") { - let nativeScaling = Math.min(powerOfTwoSampleSize, 8); - powerOfTwoWidth = Math.ceil(orientedSourceWidth / nativeScaling); - powerOfTwoHeight = Math.ceil(orientedSourceHeight / nativeScaling); - let secondaryScaling = Math.floor(powerOfTwoSampleSize / 8); - if (secondaryScaling > 0) { - powerOfTwoWidth = powerOfTwoWidth / secondaryScaling; - powerOfTwoHeight = powerOfTwoHeight / secondaryScaling; - } - } else if (fileType == "PNG") { - powerOfTwoWidth = Math.floor(orientedSourceWidth / powerOfTwoSampleSize); - powerOfTwoHeight = Math.floor(orientedSourceHeight / powerOfTwoSampleSize); - console.log('执行了没', powerOfTwoHeight, powerOfTwoWidth) - } else if (fileType == "WEBP") { - powerOfTwoWidth = Math.round(orientedSourceWidth / powerOfTwoSampleSize); - powerOfTwoHeight = Math.round(orientedSourceHeight / powerOfTwoSampleSize); - } else if ( - orientedSourceWidth % powerOfTwoSampleSize != 0 || orientedSourceHeight % powerOfTwoSampleSize != 0) { - - - // let dimensions; number[] = this.getDimensions(imageReader, options, decodeCallbacks,bitmapPool); - // powerOfTwoWidth = dimensions[0]; - // powerofTwoHeight = dimensions[1]; - } else { - powerOfTwoWidth = orientedSourceWidth / powerOfTwoSampleSize; - powerOfTwoHeight = orientedSourceHeight / powerOfTwoSampleSize; - } - // Let adjustedScaleFactor = downsampleStrategy.getScaleFactor(powerOfTwoWidth, powerOfTwoHeight, targetWidth, targetHeight); - // Density scaling is only supported if inBitmap is null prior to KitKat. Avoid setting - // densities here so we calculate the final Bitmap size correctly. - // if (Build.VERSION,SDK_INT >=Build.VERSION_CODES.KITKAT) { - // options.inTargetDensity = this.adjustTargetDensityForError(adjustedScaleFactor); - // options,inDensity = this.getDensityMultiplier(adjustedScaleFactor); - //} - // if (this.isScaling(options)){ - // options.inScaled = true; - // }else { - // options.inDensity = options.inTargetDensity =0; - // } - //} - let a: ESObject = { "targetWidth": powerOfTwoWidth, "targetHeight": powerOfTwoHeight } - return a + console.log('exactScaleFactor', exactScaleFactor) + /*安卓的模式*/ + let rounding: SampleSizeRounding = CenterOutside.getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + if (rounding == null) { + throw new Error("Cannot round with null rounding"); } - //decodeStream(imageReader:ImageReader, options: ESObject, callbacks: DecodeCallbacks, bitmapPool: ESObject) { - //if (!options.inJustDecodeBounds){ - // callbacks.onObtainBounds(); - // imageReader.stopGrowingBuffers(); - // } - // } - // getDimensions(imageReader: ImageReader, options: ESObject, decodeCallbacks: DecodeCallbacks, bitmapPool: ESObject):number[] { - // options.inJustDecodeBounds = true; - // this.decodeStream(imageReader, options, decodeCallbacks, bitmapPool); - // options.inJustDecodeBounds =false; - // return new Array(options.outWidth, options,outHeight); - // + let outWidth: number = this.round(exactScaleFactor * orientedSourceWidth); + let outHeight: number = this.round(exactScaleFactor * orientedSourceHeight); + let widthScaleFactor = Math.floor(orientedSourceWidth / outWidth); + let heightScaleFactor = Math.floor(orientedSourceHeight / outHeight); + let scaleFactor = rounding == SampleSizeRounding.MEMORY ? Math.max(widthScaleFactor, heightScaleFactor) : Math.min(widthScaleFactor, heightScaleFactor) + // 将整型的缩放因子转换为2的次幂采样大小 + let powerOfTwoSampleSize: number; + if (rounding == SampleSizeRounding.MEMORY && powerOfTwoSampleSize < (1 / exactScaleFactor)) { + powerOfTwoSampleSize = powerOfTwoSampleSize << 1; + } + // 基于上一步得出的采样大小,根据不同的图片类型,计算采样后的图片尺寸 + if (fileType == "JPEG") { + let nativeScaling = Math.min(powerOfTwoSampleSize, 8); + powerOfTwoWidth = Math.ceil(orientedSourceWidth / nativeScaling); + powerOfTwoHeight = Math.ceil(orientedSourceHeight / nativeScaling); + let secondaryScaling = Math.floor(powerOfTwoSampleSize / 8); + if (secondaryScaling > 0) { + powerOfTwoWidth = powerOfTwoWidth / secondaryScaling; + powerOfTwoHeight = powerOfTwoHeight / secondaryScaling; + } + } else if (fileType == "PNG") { + powerOfTwoWidth = Math.floor(orientedSourceWidth / powerOfTwoSampleSize); + powerOfTwoHeight = Math.floor(orientedSourceHeight / powerOfTwoSampleSize); + console.log('执行了没', powerOfTwoHeight, powerOfTwoWidth) + } else if (fileType == "WEBP") { + powerOfTwoWidth = Math.round(orientedSourceWidth / powerOfTwoSampleSize); + powerOfTwoHeight = Math.round(orientedSourceHeight / powerOfTwoSampleSize); + } else if ( + orientedSourceWidth % powerOfTwoSampleSize != 0 || orientedSourceHeight % powerOfTwoSampleSize != 0) { + + + // let dimensions; number[] = this.getDimensions(imageReader, options, decodeCallbacks,bitmapPool); + // powerOfTwoWidth = dimensions[0]; + // powerofTwoHeight = dimensions[1]; + } else { + powerOfTwoWidth = orientedSourceWidth / powerOfTwoSampleSize; + powerOfTwoHeight = orientedSourceHeight / powerOfTwoSampleSize; + } + // Let adjustedScaleFactor = downsampleStrategy.getScaleFactor(powerOfTwoWidth, powerOfTwoHeight, targetWidth, targetHeight); + // Density scaling is only supported if inBitmap is null prior to KitKat. Avoid setting + // densities here so we calculate the final Bitmap size correctly. + // if (Build.VERSION,SDK_INT >=Build.VERSION_CODES.KITKAT) { + // options.inTargetDensity = this.adjustTargetDensityForError(adjustedScaleFactor); + // options,inDensity = this.getDensityMultiplier(adjustedScaleFactor); + //} + // if (this.isScaling(options)){ + // options.inScaled = true; + // }else { + // options.inDensity = options.inTargetDensity =0; // } + //} + let a: ESObject = { "targetWidth": powerOfTwoWidth, "targetHeight": powerOfTwoHeight } + return a + } - highestOneBit(i: number): number{ - i |= (i >> 1); - i |= (i >> 2); - i |= (i >> 4); - i |= (i >> 8); - i |= (i >> 16); - return i - (i >>> 1); - } - round(value: number): number { - return Math.floor(value +0.5); - } - isRotationRequired(degreesToRotate: number): boolean { - return degreesToRotate == 90 || degreesToRotate == 270; - } - // isScaling(options: ESObject): boolean { - // return options.inTargetDensity >0 - // && options.inDensity>0 - // && options.inTargetDensity != options.inDensity; - //} - getDensityMultiplier(adjustedScaleFactor: number):number{ - return Math.round(Number.MAX_VALUE * (adjustedScaleFactor <= 1 ? adjustedScaleFactor : 1 / adjustedScaleFactor)); - } - adjustTargetDensityForError(adjustedScaleFactor:number):number{ - let densityMultiplier = this.getDensityMultiplier(adjustedScaleFactor); - let targetDensity = this.round(densityMultiplier * adjustedScaleFactor); - let scaleFactorWithError = targetDensity / densityMultiplier; - let difference = adjustedScaleFactor / scaleFactorWithError; - return this.round(difference * targetDensity); - } - } \ No newline at end of file + //decodeStream(imageReader:ImageReader, options: ESObject, callbacks: DecodeCallbacks, bitmapPool: ESObject) { + //if (!options.inJustDecodeBounds){ + // callbacks.onObtainBounds(); + // imageReader.stopGrowingBuffers(); + // } + // } + // getDimensions(imageReader: ImageReader, options: ESObject, decodeCallbacks: DecodeCallbacks, bitmapPool: ESObject):number[] { + // options.inJustDecodeBounds = true; + // this.decodeStream(imageReader, options, decodeCallbacks, bitmapPool); + // options.inJustDecodeBounds =false; + // return new Array(options.outWidth, options,outHeight); + // + // } + + highestOneBit(i: number): number { + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + return i - (i >>> 1); + } + + round(value: number): number { + return Math.floor(value + 0.5); + } + + isRotationRequired(degreesToRotate: number): boolean { + return degreesToRotate == 90 || degreesToRotate == 270; + } + + // isScaling(options: ESObject): boolean { + // return options.inTargetDensity >0 + // && options.inDensity>0 + // && options.inTargetDensity != options.inDensity; + //} + getDensityMultiplier(adjustedScaleFactor: number): number { + return Math.round(Number.MAX_VALUE * (adjustedScaleFactor <= 1 ? adjustedScaleFactor : 1 / adjustedScaleFactor)); + } + + adjustTargetDensityForError(adjustedScaleFactor: number): number { + let densityMultiplier = this.getDensityMultiplier(adjustedScaleFactor); + let targetDensity = this.round(densityMultiplier * adjustedScaleFactor); + let scaleFactorWithError = targetDensity / densityMultiplier; + let difference = adjustedScaleFactor / scaleFactorWithError; + return this.round(difference * targetDensity); + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets b/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets index 0939f90..b004153 100644 --- a/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets +++ b/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets @@ -20,7 +20,12 @@ import { RequestOption } from '../RequestOption'; import { Downsampler } from '../Downsampling/Downsampler'; export class ParseImageUtil implements IParseImage { - parseImage(imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void,request?:RequestOption) { + parseImage( + imageinfo: ArrayBuffer, + onCompleteFunction: (value: PixelMap) => void | PromiseLike, + onErrorFunction: (reason?: BusinessError | string) => void, + request?:RequestOption + ) { this.parseImageThumbnail(1, imageinfo, onCompleteFunction, onErrorFunction,request) } diff --git a/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets b/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets index 557214a..51f0843 100644 --- a/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets +++ b/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets @@ -34,6 +34,29 @@ export class SVGParseImpl implements IParseSvg { editable: true, desiredSize: defaultSize }; + imageSource.getInageInfo().then((value) => { + let hValue = Math.round(value.size.height); + let wValue = Math.round(value.size.width); + let defaultSize: image.size = { + height: hValue, + width: wValue + }; + let opts: image.DecodingOptions = { + editable: true, + desiredSize: defaultSize + }; + if (option?.downsampType.getName() !== 'DownsampleNone') { + const b: ESObject = new Downsampler().calculateScaling(imageInfo, hValue, wValue, option) + console.log("bbb-----", JSON.stringify(b)) + opts = { + editable: true, + desiredSize: { + width: b.targetWidth, + height: b.targetHeight + } + } + } + }) imageSource.createPixelMap(opts).then((pixelMap: image.PixelMap) => { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap) onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap); From 81811761e2530b3b1e821029d924a34221332f26 Mon Sep 17 00:00:00 2001 From: sijainguo Date: Mon, 29 Apr 2024 11:45:28 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E9=99=8D=E9=87=87=E6=A0=B7=20parseImage=20?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E5=85=A5=E5=8F=82=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../imageknife/requestmanage/RequestManager.ets | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets b/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets index 3a76ae4..84b7094 100644 --- a/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets +++ b/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets @@ -33,7 +33,7 @@ import { GIFFrame } from '../utils/gif/GIFFrame' import { LogUtil } from '../../imageknife/utils/LogUtil' import { BusinessError } from '@ohos.base' import { DataFetchResult } from '../networkmanage/DataFetchResult' - +import { RequestOption } from '../RequestOption'; export enum Stage { @@ -188,7 +188,7 @@ export class RequestManager { let success = (value: PixelMap) => { onComplete(value); } - this.mParseImageUtil.parseImage(arrayBuffer, success, onError,request) + this.mParseImageUtil.parseImage(arrayBuffer, success, onError,request, request) } } } @@ -331,13 +331,13 @@ export class RequestManager { let success = (value: PixelMap) => { onComplete(value); } - this.mParseImageUtil.parseImage(source, success, onError) + this.mParseImageUtil.parseImage(source, success, onError, request) }, this.options.thumbDelayTime) } else { let success = (value: PixelMap) => { onComplete(value); } - this.mParseImageUtil.parseImage(source, success, onError) + this.mParseImageUtil.parseImage(source, success, onError, request) } } } @@ -360,13 +360,13 @@ export class RequestManager { let success = (value: PixelMap) => { onComplete(value); } - this.mParseImageUtil.parseImage(source, success, onError) + this.mParseImageUtil.parseImage(source, success, onError, request) }, this.options.thumbDelayTime) } else { let success = (value: PixelMap) => { onComplete(value); } - this.mParseImageUtil.parseImage(source, success, onError) + this.mParseImageUtil.parseImage(source, success, onError, request) } } @@ -454,7 +454,7 @@ export class RequestManager { this.saveCacheAndDisk(value, filetype, onComplete, source); } } - this.mParseImageUtil.parseImage(source, success, onError) + this.mParseImageUtil.parseImage(source, success, onError, request) }, this.options.thumbDelayTime) } else { let success = (value: PixelMap) => { @@ -462,7 +462,7 @@ export class RequestManager { this.saveCacheAndDisk(value, filetype, onComplete, source); } } - this.mParseImageUtil.parseImage(source, success, onError) + this.mParseImageUtil.parseImage(source, success, onError, request) } } } From 5e7d9c7f5fe4dd84acbcc115900005043461b039 Mon Sep 17 00:00:00 2001 From: sijainguo Date: Mon, 29 Apr 2024 19:29:42 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E9=99=8D=E9=87=87=E6=A0=B7=20=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- entry/src/main/ets/pages/DownsamplingPage.ets | 147 +++ .../ets/pages/imageknifeTestCaseIndex.ets | 4 + .../resources/base/profile/main_pages.json | 3 +- library/index.ets | 4 +- .../Downsampling/BaseDownsampling.ets | 4 +- .../Downsampling/DownsampleStartegy.ets | 87 +- .../imageknife/Downsampling/Downsampler.ets | 2 +- .../ets/components/imageknife/ImageKnife.ets | 2 - .../components/imageknife/RequestOption.ets | 1100 ++++++++--------- .../components/imageknife/SendableData.ets | 22 +- .../requestmanage/RequestManager.ets | 19 +- .../imageknife/utils/ParseImageUtil.ets | 2 +- .../imageknife/utils/gif/GIFParseImpl.ets | 38 +- .../imageknife/utils/gif/IParseGif.ets | 6 +- .../imageknife/utils/svg/SVGParseImpl.ets | 15 +- 15 files changed, 755 insertions(+), 700 deletions(-) create mode 100644 entry/src/main/ets/pages/DownsamplingPage.ets diff --git a/entry/src/main/ets/pages/DownsamplingPage.ets b/entry/src/main/ets/pages/DownsamplingPage.ets new file mode 100644 index 0000000..de21508 --- /dev/null +++ b/entry/src/main/ets/pages/DownsamplingPage.ets @@ -0,0 +1,147 @@ +/* + * 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 { RequestOption, ImageKnifeGlobal, ImageKnifeOption, ImageKnifeComponent } from '@ohos/libraryimageknife' +import { BusinessError } from '@ohos.base' +import { ImageKnifeData } from '@ohos/libraryimageknife' +import { + DownsampleNone, + FitCenter +} from '@ohos/imageknife/src/main/ets/components/inageknife/Downsampling/Downsamplestartegy' + +// PixelMap transform示例 +let mRotate: number = 0; +//let mUrl= "https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVqqB" let murl = $r('app.media.pngSample'); let setting = new DownsampleNone() +let mUrl = $r('app.media.pngSample'); +let setting = new DownsampleNone() + +@Entry +@Component +struct DownsamplingPage { + @State url: string = ""; + @State mCropPixelMap?: PixelMap = undefined; + @state mRoundPixelMap?: PixelMap = undefined; + @State mCirclePixelMap?: PixelMap = undefined; + @State mCircleBorderPixelMap?: PixelMap = undefined; + @state mRotatePixelMap?: PixelMap = undefined; + @state mSquarePixelMap?: PixelMap = undefined; + @state mSquarePixelMap1?: PixelMap = undefined; + @state mClipCenterPixelMap?: PixelMap = undefined; + @state mClipBottomPixelMap?: PixelMap = undefined; + @state mGrayscalePixelMap?: PixelMap = undefined; + @state mBrightnessPixelMap?: PixelMap = undefined; + @State mContrastPixelMap?: PixelMap = undefined; + @state mInvertPixelMap?: PixelMap = undefined; + @state mSepiaPixelMap?: PixelMap = undefined; + @State mSketchPixelMap?: PixelMap = undefined; + @state mBlurPixelMap?: PixelMap = undefined; + @state mPixelPixelMap?: PixelMap = undefined; + @State mSwirlPixelMap?: PixelMap = undefined; + @State mMaskPixelMap?: PixelMap = undefined; + @State mKuwaharaPixelMap?: PixelMap = undefined; + @State mToonPixelMap?: PixelMap = undefined; + @State mVignettePixelMap?: PixelMap = undefined; + @State BytesNumber: number = 0; + @State BytesNumber1: number = 0; + @state imageKnifeOption2: ImageKnifeOption = + { + loadSrc: $r('app.media.gifSample'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + downsampling: new DownsampleNone() + }; + + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) { + Column() { + Column() { + Column() { + Text("大小:" + this.BytesNumber).fontColor(Color.Gray).fontsize(16); + Button() { + Text($r("app.string.trans_square")).fontSize(13).fontColor(Color.White) + } + .height(35) + .width(120) + .margin({ top: 10 }) + .onclick(() => { + this.transformSquare(); + }); + if (this.mSquarePixelMap) { + Image(this.mSquarePixelMap == undefined ? "" : this.mSquarePixelMap!) + .objectFit(ImageFit.Fill) + .width(200) + .height(200) + .margin({ top: 10 }) + } + }.margin({ top: 10 }); + }.margin({ bottom: 30 }); + Column() { + Column() { + Text("大小:" + this.BytesNumber1).fontColor(Color.Gray).fontSize(16); + Button() { + Text('降采样').fontsize(13).fontColor(Color.White) + } + .height(35).width(120) + .margin({ top: 10 }) + .onClick(() => { + this.transformSquare1(); + }); + if (this.mSquarePixelMap1) { + Image(this.mSquarePixelMap1 == undefined ? '' : this.mSquarePixelMap1!) + .objectFit(ImageFit.Fill) + .width(200) + .height(200) + .margin({ top: 10 }) + } + }.margin({ top: 10 }); + }.margin({ bottom: 30 }); + Column() { + column() { + Text("ImageKnifeComponent用法").fontColor(Color.Gray).fontSize(16); + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption2 }) + .width(200) + .height(200) + }.margin({ top: 10 }); + }.margin({ bottom: 30 }); + } + }.width('100%').height('100%'); + } + + //正方形裁剪 + transformSquare() { + let imageKnifeOption = new RequestOption(); + imageKnifeOption.load(murl) + .addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => { + this.mSquarePixelMap = data.drawPixelMap?.imagePixelMap as PixelMap; + this.BytesNumber = (data.drawPixelMap?.imagePixelMap as PixelMap).getPixelBytesNumber() + return false; + } }) + ImageKnife?.call(imageKnifeOption); + } + + transformSquare1() { + let imageKnifeOption = new RequestOption(); + imageKnifeOption.load(mUrl) + .addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => { + this.mSquarePixelMap1 = data.drawPixelMap?.imagePixelMap as PixelMap; + this.BytesNumber1 = (data.drawPixelMap?.imagePixelMap as PixelMap).getPixelBytesNumber() + return false; + } }) + .setImageViewsize({ width: 800, height: 500 }) + .downsampleStrategy(new FitCenter()) + ImageKnife?.call(imageKnifeOption); + } +} + +let ImageKnife = ImageKnifeGlobal.getInstance().getImageKnife(); \ No newline at end of file diff --git a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets index a951647..97ad633 100644 --- a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets +++ b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets @@ -40,6 +40,10 @@ struct IndexFunctionDemo { console.log("优先级加载") router.pushUrl({ url: "pages/testPriorityComponent" }); }).margin({ top: 5, left: 3 }) + Button("下采样加载") + .onClick(() => { + router.pushUrl({ url: "pages/DownsamplingPage" }); + }).margin({ top: 5, left: 3 }) }.width('100%').height(60).backgroundColor(Color.Pink) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index 3be410f..d678384 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -56,6 +56,7 @@ "pages/testImageKnifeHeic", "pages/testImageKnifeNetPlaceholder", "pages/testCustomDataFetchClientWithPage", - "pages/testReuseAblePages" + "pages/testReuseAblePages", + "pages/DownsamplingPage" ] } \ No newline at end of file diff --git a/library/index.ets b/library/index.ets index 5e8d3ca..2316b6e 100644 --- a/library/index.ets +++ b/library/index.ets @@ -130,4 +130,6 @@ export { GIFFrame } from './src/main/ets/components/imageknife/utils/gif/GIFFram export { IDrawLifeCycle } from './src/main/ets/components/imageknife/interface/IDrawLifeCycle' // 日志管理 -export { LogUtil } from './src/main/ets/components/imageknife/utils/LogUtil' \ No newline at end of file +export { LogUtil } from './src/main/ets/components/imageknife/utils/LogUtil' +/*下采样*/ +export {Downsampler} from './src/main/ets/components/imageknife/Downsampling/Downsampler' \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets b/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets index 9ade775..ec51f3c 100644 --- a/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets +++ b/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets @@ -1,6 +1,6 @@ import {lang}from '@kit.ArkTs'; -type ISendable=lang.ISendable; -export interface BaseDownsampling{ +type ISendable = lang.ISendable; +export interface BaseDownsampling extends ISendable{ getName():string getScaleFactor(sourceWidth:number, sourceHeight:number, requestWidth:number, requestHeight:number):number diff --git a/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets b/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets index 3d6583d..4968d18 100644 --- a/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets +++ b/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets @@ -2,17 +2,18 @@ import { BaseDownsampling } from './BaseDownsampling' @Sendable export class CenterInside{ - getName() { return "CenterInside"} - getScaleFactor(sourceWidth:number, sourceHeight:number,requestWidth:number, requestHeight:number):number { - - return Math.min(1,FitCenter.getScaleFactor(sourceWidth, sourceHeight, requestWidth, requestHeight)) - } - getSampleSizeRounding(sourceWidth:number, sourceHeight:number, requestWidth:number, requestHeight:number):SampleSizeRounding { - return this.getScaleFactor(sourceWidth, sourceHeight, requestWidth, requestHeight)==1 - ?SampleSizeRounding.QUALITY - :FitCenter.getSampleSizeRounding(sourceWidth, sourceHeight, requestWidth, requestHeight) - - } + getName() { return "CenterInside"} + getScaleFactor(sourceWidth:number, sourceHeight:number,requestWidth:number, requestHeight:number):number { + return Math.min(1,this.getScale(sourceWidth, sourceHeight, requestWidth, requestHeight)) + } + getSampleSizeRounding(sourceWidth:number, sourceHeight:number, requestWidth:number, requestHeight:number):SampleSizeRounding { + return 1 + } + getScale(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { + let widthPercentage =requestWidth/sourceWidth + let heightPercentage =requestHeight/sourceHeight + return Math.min(widthPercentage,heightPercentage) + } } /*不进行下采样*/ @@ -26,44 +27,62 @@ export class DownsampleNone implements BaseDownsampling{ } getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - return SampleSizeRounding.QUALITY + return 1 } } /* 下采样使得图像的组大尺寸在给定的尺寸的1/2之间*/ @Sendable - export class AtMost implements BaseDownsampling{ +export class AtMost implements BaseDownsampling{ getName(){ return "AtMost" } - getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth:number,requestHeight: number): number { - let maxIntegerFactor=Math.ceil(Math.max(sourceHeight/requestHeight,sourceWidth/requestWidth)); - let lesserOrEqualSampleSize = Math.max(1,highestOneBit(maxIntegerFactor)) - let greaterOrEqualSampleSize = lesserOrEqualSampleSize<<(lesserOrEqualSampleSize> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + return i - (i >>> 1); + } +} @Sendable export class Atleast implements BaseDownsampling{ getName(){ return "Atleast" } - getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - let minIntegerFactor=Math.floor(Math.min(sourceHeight/requestHeight,sourceWidth/requestWidth)) - - return minIntegerFactor==0?1:1/highestOneBit(minIntegerFactor) - } + getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { + let minIntegerFactor=Math.floor(Math.min(sourceHeight/requestHeight,sourceWidth/requestWidth)) + return minIntegerFactor==0?1:1/this.highestOneBit(minIntegerFactor) + } getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - return SampleSizeRounding.QUALITY - } + return 1 + } + highestOneBit(i: number): number { + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + return i - (i >>> 1); + } } @Sendable export class CenterOutside implements BaseDownsampling{ + static getSampleSizeRounding(orientedSourceWidth: number, orientedSourceHeight: number, targetWidth: number, targetHeight: number): SampleSizeRounding { + throw new Error('Method not implemented.') + } getName(){ return "CenterOutside" } @@ -74,7 +93,7 @@ export class CenterOutside implements BaseDownsampling{ } getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - return SampleSizeRounding.QUALITY + return 1 } } @@ -84,13 +103,13 @@ export class FitCenter{ getName(){ return "FitCenter" } - public static getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { + getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { let widthPercentage =requestWidth/sourceWidth let heightPercentage =requestHeight/sourceHeight return Math.min(widthPercentage,heightPercentage) } - public static getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - return SampleSizeRounding.MEMORY + getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { + return 1 } } diff --git a/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets b/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets index b2cfa5f..202ee00 100644 --- a/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets +++ b/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets @@ -52,7 +52,7 @@ export class Downsampler { let heightScaleFactor = Math.floor(orientedSourceHeight / outHeight); let scaleFactor = rounding == SampleSizeRounding.MEMORY ? Math.max(widthScaleFactor, heightScaleFactor) : Math.min(widthScaleFactor, heightScaleFactor) // 将整型的缩放因子转换为2的次幂采样大小 - let powerOfTwoSampleSize: number; + let powerOfTwoSampleSize: number = 0; if (rounding == SampleSizeRounding.MEMORY && powerOfTwoSampleSize < (1 / exactScaleFactor)) { powerOfTwoSampleSize = powerOfTwoSampleSize << 1; } diff --git a/library/src/main/ets/components/imageknife/ImageKnife.ets b/library/src/main/ets/components/imageknife/ImageKnife.ets index 8108f8b..6ab3b9b 100644 --- a/library/src/main/ets/components/imageknife/ImageKnife.ets +++ b/library/src/main/ets/components/imageknife/ImageKnife.ets @@ -810,8 +810,6 @@ async function taskExecute(sendData:SendableData,taskData:TaskParams): Promise

void -} +export class RequestManager { + private TAG: string = "RequestManager"; + private options: RequestOption; + private mIDataFetch: IDataFetch; + private mIResourceFetch: IResourceFetch; + private mParseImageUtil: IParseImage; + private diskMemoryCache: DiskLruCache; -export enum Priority { - HIGH = 0, - MEDIUM = 1, - LOW = 2 -} -export class RequestOption { - // 遍历添加图片http请求头 - headers: Map = new Map(); + constructor(option: RequestOption,diskMemoryCache: DiskLruCache, dataFetch: IDataFetch, resourceFetch: IResourceFetch) { + this.options = option; - // RequestOption调用header对于的方法 - addHeader(key: string, value: Object) { - this.headers.set(key, value); + this.diskMemoryCache = diskMemoryCache; + // 网络下载能力 + this.mIDataFetch = dataFetch; + + // 本地数据解析能力 + this.mIResourceFetch = resourceFetch; + + // 解析image能力 + this.mParseImageUtil = new ParseImageUtil(); } - // 全局调用header对应的方法,包含RequestOption的形式 - addHeaderMap(map: Map) { - map.forEach((value, key) => { - if (!this.headers.has(key)) { - this.addHeader(key, value); - } - }) + // DecodeJob work + private mStage: Stage = Stage.INITIALIZE; + private mRunReason: RunReason = RunReason.INITIALIZE; + process = (onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) => { + LogUtil.log("ImageKnife RequestManager process !"); + this.loadLeve1MemoryCache(onComplete, onError) } - // 优先级 - priority: Priority = Priority.MEDIUM; - uuid: string = '' // 唯一标识 - loadSrc: string | PixelMap | Resource = ''; - strategy: DiskStrategy = new AUTOMATIC(); - //下采样相关 - downsampType: BaseDownsampling = new BaseDownsamplings() - dontAnimateFlag = false; - placeholderSrc: string | PixelMap | Resource | undefined = undefined; - placeholderFunc: AsyncSuccess | undefined = undefined; - errorholderSrc: PixelMap | Resource | undefined = undefined; - errorholderFunc: AsyncSuccess | undefined = undefined; - errorholderData: ImageKnifeData | undefined = undefined; - thumbSizeMultiplier: number = 0; - // 如果存在缩略图,则主图延时1s加载 - thumbDelayTime: number = 1000 - thumbHolderFunc: AsyncSuccess | undefined = undefined; - requestListeners: Array> | undefined = undefined; - // 进度条 - progressFunc: AsyncSuccess | undefined = undefined; - // 后备回调符 - fallbackSrc: PixelMap | Resource | undefined = undefined; - fallbackFunc: AsyncSuccess | undefined = undefined; - fallbackData: ImageKnifeData | undefined = undefined; - // 重试图层 - retryholderSrc: PixelMap | Resource | undefined = undefined; - retryholderFunc: AsyncSuccess | undefined = undefined; - retryholderData: ImageKnifeData | undefined = undefined; - size: Size = { width: -1, height: -1 }; - // 网络下载数据回调 - allCacheInfoCallback: IAllCacheInfoCallback | undefined = undefined; - onlyRetrieveFromCache: boolean = false; - isCacheable: boolean = true; - // 开启GPU变换绘制 - gpuEnabled: boolean = false; - // 变换相关 - transformations: Array> = new Array(); - generateCacheKey: string = ""; - generateResourceKey: string = ""; - generateDataKey: string = ""; - filesPath: string = ""; // data/data/包名/files目录 - cachesPath: string = ""; // 网络下载默认存储在data/data/包名/cache/ImageKnifeNetworkFolder/目标md5.img下面 - placeholderCacheKey: string = ""; - retryholderCacheKey: string = ""; - errorholderCacheKey: string = ""; - // string类型占位图相关缓存key值 - placeholderRegisterCacheKey: string = ""; - placeholderRegisterMemoryCacheKey: string = ""; - fallbackCacheKey: string = ""; - // 自定义缓存关键字 - signature?: ObjectKey; - // 设置是否使用应用自定义的方式加载图片 - customGetImage?: (context: Context, src: string) => Promise; - // 下载原始文件地址 - downloadFilePath: string = ""; - //磁盘缓存文件路径 - diskMemoryCachePath: string =""; - // 网络文件下载统一存放 - networkCacheFolder: string = "ImageKnifeNetworkFolder" - // 主线图片 状态变化 是否加载完成 - // 主图未加载成功 显示占位图 主图加载成功不展示占位图 - loadMainReady = false; - // 失败占位图展示状态 当true 表示主图加载失败需要展示失败占位图 - loadErrorReady = false; - // 重试占位图展示状态 当true 表示主图加载失败需要展示失败占位图 - loadRetryReady = false; - // 后备回调符展示状态 当true 表占位图加载失败需要展示后备回调符 - loadFallBackReady = false; - // 缩略图展示 - loadThumbnailReady = false; - detachFromLayout: DetachFromLayout = { - detach: () => { - let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); - if (imageKnife != undefined) { - imageKnife.remove(this.uuid); - } + private runWrapped(request: RequestOption, runReason: RunReason, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager runWrapped") + if (runReason == RunReason.INITIALIZE) { + this.mStage = this.getNextStage(request, this.mStage); + this.searchLoadFrom(this.options, this.mStage, onComplete, onError); + } else { + throw new Error("Unrecognized run reason: " + runReason) } } - // module资源的需要当前的module context - moduleContext?: common.UIAbilityContext = undefined; - constructor() { - // 初始化全局监听 - this.requestListeners = new Array(); + private getNextStage(request: RequestOption, current: Stage): Stage { + if (current == Stage.INITIALIZE) { + return request.strategy.decodeCachedResource() + ? Stage.RESOURCE_CACHE + : this.getNextStage(request, Stage.RESOURCE_CACHE); + } else if (current == Stage.RESOURCE_CACHE) { + return request.strategy.decodeCachedData() + ? Stage.DATA_CACHE + : this.getNextStage(request, Stage.DATA_CACHE); + } else if (current == Stage.DATA_CACHE) { + return request.onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE; + } else if (current == Stage.SOURCE) { + return Stage.FINISHED; + } else if (current == Stage.FINISHED) { + return Stage.FINISHED; + } else { + throw new Error("ImageKnife Unrecognized stage: " + current); + } + } - let ctx = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext + // 究竟从哪里加载数据 + private searchLoadFrom(request: RequestOption, current: Stage, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager searchLoadFrom") + if (current == Stage.RESOURCE_CACHE) { + this.loadDiskFromTransform(request, onComplete, onError); + } else if (current == Stage.DATA_CACHE) { + this.loadDiskFromSource(request, onComplete, onError); + } else if (current == Stage.SOURCE) { + this.parseSource(request, onComplete, onError) + } else if (current == Stage.FINISHED) { + onError("在仅从缓存获取数据中,未获取到数据!") + } else { + throw new Error("Unrecognized stage: " + current); + } + } + + // 加载网络资源 + private async loadSourceFromNetwork(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + try { + LogUtil.log("ImageKnife RequestManager loadSourceFromNetwork") + let result: DataFetchResult = await this.mIDataFetch.loadData(request); + if(result.error){ + onError(result.error) + }else{ + if(result.data){ + this.downloadSuccess(request, result.data, onComplete, onError) + }else{ + onError("datafetch data is null") + } + } + } catch (e) { + LogUtil.error("ImageKnife RequestManager loadSourceFromNetwork error") + } + } + + // 加载本地资源 + private loadSourceFormNative(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager loadSourceFormNative") + // 本地解析后进行一级缓存 + let success = (arrayBuffer: ArrayBuffer) => { + // 使用媒体子系统 ImageSource解析文件 获取PixelMap + let fileTypeUtil = new FileTypeUtil(); + let typeValue = fileTypeUtil.getFileType(arrayBuffer) + LogUtil.log("ImageKnife RequestManager - 文件类型为= " + typeValue) + // gif、webp处理 + if ((ImageKnifeData.GIF == typeValue || ImageKnifeData.WEBP == typeValue) && !request.dontAnimateFlag) { + // 处理gif、webp + this.gifProcess(onComplete, onError, arrayBuffer, typeValue) + } else if (ImageKnifeData.SVG == typeValue) { + // 处理svg + this.svgProcess(request, onComplete, onError, arrayBuffer, typeValue) + } else { + if (request.transformations[0]) { + request.transformations[0].transform(arrayBuffer, request, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + // 输出给Image + if (pixelMap) { + onComplete(pixelMap); + } else { + onError(error); + } + } + }) + } + else { + let success = (value: PixelMap) => { + onComplete(value); + } + this.mParseImageUtil.parseImage(arrayBuffer, success, onError,request) + } + } + } + let ctx = request.getModuleContext(); if (ctx != undefined) { - this.moduleContext = ctx; + this.mIResourceFetch.loadResource(ctx, request.loadSrc as Resource, success, onError); + } else { + onError('ImageKnife RequestManager loadSourceFormNative moduleContext is undefined! please check it') + } + + } + + // 加载磁盘缓存 原图 + private loadDiskFromSource(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager loadDiskFromSource") + let cached = this.diskMemoryCache.get(request.generateDataKey); + if (cached != null && cached.byteLength > 0) { + LogUtil.log("ImageKnife loadDiskFromSource load resource from DiskLruCache") + this.parseDiskFile2PixelMap(request, cached, onComplete, onError) + } else { + this.mStage = Stage.SOURCE; + this.mStage = request.onlyRetrieveFromCache? Stage.FINISHED : Stage.SOURCE + this.searchLoadFrom(this.options, this.mStage, onComplete, onError); } } - setPriority(priority: Priority) { - this.priority = priority - } - setTransformations( array:Array>){ - this.transformations = array; - } - generateUUID(): string { - return SparkMD5.hashBinary(JSON.stringify(this.loadSrc)) as string; - } - setModuleContext(moduleCtx: common.UIAbilityContext) { - this.moduleContext = moduleCtx; - } - - getModuleContext(): common.UIAbilityContext | undefined { - return this.moduleContext; - } - - /** - * set image Component size - */ - setImageViewSize(imageSize: Size) { - this.size.width = imageSize.width; - this.size.height = imageSize.height; - return this; - } - - getFilesPath() { - return this.filesPath; - } - - setFilesPath(path: string) { - this.filesPath = path; - } - - getCachesPath() { - return this.cachesPath; - } - - setCachesPath(path: string) { - this.cachesPath = path; - } - - load(src: string | PixelMap | Resource) { - this.loadSrc = src; - // 初始化唯一标识,可以用这个标识找到ImageKnife中的对象 - this.uuid = this.generateUUID(); - return this; - } - - diskCacheStrategy(strategy: DiskStrategy) { - this.strategy = strategy; - return this; - } - - dontAnimate() { - this.dontAnimateFlag = true; - return this; - } - - // 仅支持 本地图片 - placeholder(src: string | PixelMap | Resource, func?: AsyncSuccess) { - this.placeholderSrc = src; - this.placeholderFunc = func; - return this; - } - - errorholder(src: PixelMap | Resource, func?: AsyncSuccess) { - this.errorholderSrc = src; - this.errorholderFunc = func; - return this; - } - - fallback(src: PixelMap | Resource, func?: AsyncSuccess) { - this.fallbackSrc = src; - this.fallbackFunc = func; - return this; - } - - retryholder(src: PixelMap | Resource, func?: AsyncSuccess) { - this.retryholderSrc = src; - this.retryholderFunc = func; - return this; - } - - thumbnail(sizeMultiplier: number, func?: AsyncSuccess, displayTime?: number) { - this.thumbSizeMultiplier = sizeMultiplier; - this.thumbHolderFunc = func; - if (displayTime) { - this.thumbDelayTime = displayTime; + // 加载磁盘缓存 变换后图片 + private loadDiskFromTransform(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager loadDiskFromTransform") + let cached = this.diskMemoryCache.get(request.generateResourceKey); + if (cached != null) { + LogUtil.log("ImageKnife loadDiskFromTransform load resource from DiskLruCache") + this.parseDiskTransformFile2PixelMap(request, cached, onComplete, onError) + } else { + this.mStage = Stage.DATA_CACHE; + this.searchLoadFrom(this.options, this.mStage, onComplete, onError); } - return this; } - addProgressListener(func?: AsyncSuccess) { - this.progressFunc = func; - return this; - } - - addListener(func: AsyncCallback) { - if (this.requestListeners != undefined) { - this.requestListeners?.push(func); + parseSource(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager parseSource") + try { + if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') { + // PixelMap 外层捕获效率更高,不会进入这里 + } else if (typeof request.loadSrc == 'string') { + this.loadSourceFromNetwork(request, onComplete, onError); + } else { + let res = request.loadSrc as Resource; + if (typeof res.id != 'undefined') { + this.loadSourceFormNative(request, onComplete, onError) + } else { + LogUtil.log("输入参数有问题!") + } + } + } catch (e) { + LogUtil.error("ImageKnife RequestManager parseSource error") } - return this; + } - addAllCacheInfoCallback(func: IAllCacheInfoCallback) { - this.allCacheInfoCallback = func; - return this; + private loadLeve1MemoryCache(onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + try { + this.runWrapped(this.options, this.mRunReason, onComplete, onError) + } catch (e) { + LogUtil.error("ImageKnife loadLeve1MemoryCache error") + } } - skipMemoryCache(skip: boolean) { - this.isCacheable = !skip; - return this; + // 解析磁盘文件变成PixeMap + private parseDiskFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager parseDiskFile2PixelMap") + // 步骤一:文件转为pixelMap 然后变换 给Image组件 + let fileTypeUtil = new FileTypeUtil(); + let typeValue = fileTypeUtil.getFileType(source); + // 解析磁盘文件 gif、webp 和 svg + if ((ImageKnifeData.GIF == typeValue || ImageKnifeData.WEBP == typeValue) && !request.dontAnimateFlag) { + // 处理gif、webp + this.gifProcess(onComplete, onError, source, typeValue) + } else if (ImageKnifeData.SVG == typeValue) { + this.svgProcess(request, onComplete, onError, source, typeValue) + } else { + if (this.options.transformations[0]) { + if (this.options.thumbSizeMultiplier) { + let thumbOption: RequestOption = new RequestOption(); + thumbOption.setImageViewSize({ + width: Math.round(this.options.thumbSizeMultiplier * this.options.size.width), + height: Math.round(this.options.thumbSizeMultiplier * this.options.size.height) + }) + let ctx = this.options.getModuleContext() + if (ctx != undefined) { + thumbOption.setModuleContext(ctx) + } else { + onError('RequestManager parseDiskFile2PixelMap moduleContext is undefined, please check it!') + return + } + let thumbCallback = this.options.thumbholderOnComplete; + let thumbError = this.options.thumbholderOnError; + this.options.transformations[0].transform(source, thumbOption, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + thumbCallback(pixelMap); + } else { + thumbError(error); + } + } + }) + setTimeout(() => { + this.options.transformations[0].transform(source, request, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + onComplete(pixelMap); + } else { + onError(error); + } + } + }) + }, this.options.thumbDelayTime); + } + else { + this.options.transformations[0].transform(source, request, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + onComplete(pixelMap); + } else { + onError(error); + } + } + }) + } + } else { + // thumbnail 缩略图部分 + if (request.thumbSizeMultiplier) { + let thumbCallback = this.options.thumbholderOnComplete + let thumbError = this.options.thumbholderOnError + let thumbSuccess = (value: PixelMap) => { + thumbCallback(value); + } + this.mParseImageUtil.parseImageThumbnail(request.thumbSizeMultiplier, source, thumbSuccess, thumbError); + setTimeout(() => { + let success = (value: PixelMap) => { + onComplete(value); + } + this.mParseImageUtil.parseImage(source, success, onError, request) + }, this.options.thumbDelayTime) + } else { + let success = (value: PixelMap) => { + onComplete(value); + } + this.mParseImageUtil.parseImage(source, success, onError, request) + } + } + } } - retrieveDataFromCache(flag: boolean) { - this.onlyRetrieveFromCache = flag; + // 解析磁盘变换后文件变成PixeMap + private parseDiskTransformFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager parseDiskTransformFile2PixelMap") + let fileTypeUtil = new FileTypeUtil(); + let typeValue = fileTypeUtil.getFileType(source); + // thumbnail 缩略图部分 + if (request.thumbSizeMultiplier) { + let thumbCallback = this.options.thumbholderOnComplete + let thumbError = this.options.thumbholderOnError + let thumbSuccess = (value: PixelMap) => { + thumbCallback(value); + } + this.mParseImageUtil.parseImageThumbnail(request.thumbSizeMultiplier, source, thumbSuccess, thumbError); + setTimeout(() => { + let success = (value: PixelMap) => { + onComplete(value); + } + this.mParseImageUtil.parseImage(source, success, onError, request) + }, this.options.thumbDelayTime) + } else { + let success = (value: PixelMap) => { + onComplete(value); + } + this.mParseImageUtil.parseImage(source, success, onError, request) + } } - rotateImage(degreesToRotate: number | undefined) { - if (degreesToRotate == undefined) { + private downloadSuccess(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + + if (source == null || source == undefined || source.byteLength <= 0) { + onError('ImageKnife Download task completed. but download file is empty!') return } - let rotateImage = new RotateImageTransformation(degreesToRotate); - this.transformations.push(rotateImage); - return this; + + // 下载成功之后 去data/data/包名/唯一路径/文件名 读取数据 + // 步骤一:文件转为pixelMap 然后变换 给Image组件 + // 步骤二: 文件名保存一份全局 + // 步骤三:查看文件是否支持 非支持类型直接返回 + let fileTypeUtil = new FileTypeUtil(); + let filetype: string | null = fileTypeUtil.getFileType(source); + if (filetype == null) { + onError("下载文件解析后类型为null,请检查数据源!"); + return; + } + + if (!fileTypeUtil.isImage(source)) { + onError("暂不支持 下载文件类型!类型=" + filetype); + return; + } + + // 解析磁盘文件 gif、webp 和 svg + if ((ImageKnifeData.GIF == filetype || ImageKnifeData.WEBP == filetype) && !this.options.dontAnimateFlag) { + // 处理gif、webp + this.gifProcess(onComplete, onError, source, filetype) + + // 保存二级磁盘缓存 + Promise.resolve(source) + .then((arraybuffer: ArrayBuffer) => { + this.diskMemoryCache.set(this.options.generateDataKey,arraybuffer); + }) + .catch((err: BusinessError) => { + LogUtil.log('download file is =' + ImageKnifeData.GIF + 'and save diskLruCache error =' + (err as BusinessError)) + }) + } else if (ImageKnifeData.SVG == filetype) { + // 处理svg + this.svgProcess(request, onComplete, onError, source, filetype) + + // 保存二级磁盘缓存 + Promise.resolve(source) + .then((arraybuffer: ArrayBuffer) => { + this.diskMemoryCache.set(this.options.generateDataKey,arraybuffer); + }) + .catch((err: BusinessError) => { + LogUtil.log('download file is =' + ImageKnifeData.SVG + 'and save diskLruCache error =' + (err as BusinessError)) + }) + } else { + // 进行变换 + if (this.options.transformations[0]) { + // thumbnail 缩略图部分 + if (this.options.thumbSizeMultiplier) { + if (filetype != null) { + this.thumbnailProcess(source, filetype, onComplete, onError); + } + } else { + this.options.transformations[0].transform(source, this.options, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + if (filetype != null) { + this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); + } + } else { + onError(error); + } + } + }) + } + } else { + // thumbnail 缩略图部分 + if (this.options.thumbSizeMultiplier) { + let thumbCallback = this.options.thumbholderOnComplete + let thumbError = this.options.thumbholderOnError + let thumbSuccess = (value: PixelMap) => { + thumbCallback(value); + } + this.mParseImageUtil.parseImageThumbnail(this.options.thumbSizeMultiplier, source, thumbSuccess, thumbError); + setTimeout(() => { + let success = (value: PixelMap) => { + if (filetype != null) { + this.saveCacheAndDisk(value, filetype, onComplete, source); + } + } + this.mParseImageUtil.parseImage(source, success, onError, request) + }, this.options.thumbDelayTime) + } else { + let success = (value: PixelMap) => { + if (filetype != null) { + this.saveCacheAndDisk(value, filetype, onComplete, source); + } + } + this.mParseImageUtil.parseImage(source, success, onError, request) + } + } + } } - centerCrop() { - this.transformations.push(new CenterCrop()); - return this; + createImagePixelMap(imageKnifeType: ImageKnifeType, imageKnifeValue: PixelMap): ImageKnifeData { + return ImageKnifeData.createImagePixelMap(imageKnifeType, imageKnifeValue); } - centerInside() { - this.transformations.push(new CenterInside()); - return this; + createImageGIFFrame(imageKnifeType: ImageKnifeType, imageKnifeValue: GIFFrame[]): ImageKnifeData { + return ImageKnifeData.createImageGIFFrame(imageKnifeType, imageKnifeValue); } - fitCenter() { - this.transformations.push(new FitCenter()); - return this; + + private async saveCacheAndDisk(value: PixelMap, filetype: string, onComplete: (value: PixelMap) => void | PromiseLike, source: ArrayBuffer) { + let save2DiskCache = (arraybuffer: ArrayBuffer) => { + this.diskMemoryCache.set(this.options.generateDataKey,arraybuffer); + // 落盘之后需要主动移除当前request并且调用下一个加载 + let removeCurrentAndSearchNextRun = this.options.removeCurrentAndSearchNext; + removeCurrentAndSearchNextRun(); + } + let runSave2Disk = (resolve: (value: ArrayBuffer) => void | PromiseLike, reject: (reason?: BusinessError | string) => void) => { + resolve(source); + } + let promise = new Promise(runSave2Disk); + await promise.then(save2DiskCache); + onComplete(value); } - roundedCorners(obj: RoundCorner | undefined) { - if (obj == undefined) { + thumbnailProcess(source: ArrayBuffer, filetype: string, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + let thumbOption = new RequestOption(); + let ctx = this.options.getModuleContext() + if (ctx != undefined) { + thumbOption.setModuleContext(ctx) + } else { + onError('RequestManager thumbnailProcess moduleContext is undefined, please check it!') return } - let transformation = new RoundedCornersTransformation({ - top_left: obj.top_left, - top_right: obj.top_right, - bottom_left: obj.bottom_left, - bottom_right: obj.bottom_right + thumbOption.setImageViewSize({ + width: Math.round(this.options.thumbSizeMultiplier * this.options.size.width), + height: Math.round(this.options.thumbSizeMultiplier * this.options.size.height) }) - this.transformations.push(transformation); - return this; - } - - cropCircle() { - let transformation = new CropCircleTransformation() - this.transformations.push(transformation); - return this; - } - - cropCircleWithBorder(border: number | undefined, obj: rgbColor | undefined) { - if (border == undefined || obj == undefined) { - return - } - let transformation = new CropCircleWithBorderTransformation(border, obj) - this.transformations.push(transformation); - return this; - } - - cropSquare() { - let transformation = new CropSquareTransformation() - this.transformations.push(transformation); - return this; - } - - crop(width: number | undefined, height: number | undefined, cropType: CropType | undefined) { - if (width == undefined || height == undefined || cropType == undefined) { - return - } - let transformation = new CropTransformation(width, height, cropType) - this.transformations.push(transformation); - return this; - } - - grayscale() { - let transformation = new GrayscaleTransformation() - this.transformations.push(transformation); - return this; - } - - brightnessFilter(brightness: number | undefined) { - if (brightness == undefined) { - return - } - let transformation = new BrightnessFilterTransformation(brightness) - this.transformations.push(transformation); - return this; - } - - contrastFilter(contrast: number | undefined) { - if (contrast == undefined) { - return - } - let transformation = new ContrastFilterTransformation(contrast) - this.transformations.push(transformation); - return this; - } - - invertFilter() { - let transformation = new InvertFilterTransformation() - this.transformations.push(transformation); - return this; - } - - sepiaFilter() { - let transformation = new SepiaFilterTransformation() - this.transformations.push(transformation); - return this; - } - - sketchFilter() { - let transformation = new SketchFilterTransformation() - this.transformations.push(transformation); - return this; - } - - blur(radius: number | undefined, sampling?: number) { - if (radius == undefined) { - return - } - if (sampling == undefined) { - let transformation = new BlurTransformation(radius) - this.transformations.push(transformation); - } else { - let transformation = new BlurTransformation(radius, sampling) - this.transformations.push(transformation); - } - return this; - } - - pixelationFilter(pixel: number | undefined) { - if (pixel == undefined) { - return - } - let transformation = new PixelationFilterTransformation(pixel) - this.transformations.push(transformation); - return this; - } - - swirlFilter(degree: number | undefined) { - if (degree == undefined) { - return - } - let transformation = new SwirlFilterTransformation(degree) - this.transformations.push(transformation); - return this; - } - - mask(maskResource: Resource | undefined) { - if (maskResource == undefined) { - return - } - let transformation = new MaskTransformation(maskResource) - this.transformations.push(transformation); - return this; - } - - kuwaharaFilter(radius: number | undefined) { - if (radius == undefined) { - return - } - let transformation = new KuwaharaFilterTransform(radius); - this.transformations.push(transformation); - return this; - } - - toonFilter(threshold: number | undefined, quantizationLevels: number | undefined) { - if (threshold == undefined || quantizationLevels == undefined) { - return - } - let transformation = new ToonFilterTransform(threshold, quantizationLevels); - this.transformations.push(transformation); - return this; - } - - vignetteFilter(centerPoint: Array | undefined, vignetteColor: Array | undefined, vignetteSpace: Array | undefined) { - if (centerPoint == undefined || vignetteColor == undefined || vignetteSpace == undefined) { - return - } - let transformation = new VignetteFilterTransform(centerPoint, vignetteColor, vignetteSpace); - this.transformations.push(transformation); - return this; - } - - transform(input: BaseTransform) { - this.transformations.push(input); - return this; - } - - transforms(inputs: BaseTransform[]) { - this.transformations = inputs; - return this; - } - - // 开启GPU变换绘制 - enableGPU() { - this.gpuEnabled = true; - return this; - } - downsampleStrategy(downsampType: ESObject){ - this.downsampType = downsampType - return this; - } - - // 占位图解析成功 - placeholderOnComplete = (imageKnifeData:ImageKnifeData) => { - LogUtil.log("placeholderOnComplete has called!"); - LogUtil.log("Main Image is Ready:" + this.loadMainReady); - if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady) && !this.loadThumbnailReady) { - // 主图未加载成功,并且未加载失败 显示占位图 主图加载成功或者加载失败后=>不展示占位图 - if (this.placeholderSrc != undefined) { - this.placeholderFunc?.asyncSuccess(imageKnifeData) - } - } - } - // 占位图解析失败 - placeholderOnError = (error: BusinessError | string) => { - LogUtil.log("占位图解析失败 error =" + error) - } - // 缩略图解析成功 - thumbholderOnComplete = (value: PixelMap | GIFFrame[]) => { - let imageKnifeData = new ImageKnifeData(); - if ((typeof (value as PixelMap).isEditable) == 'boolean') { - imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value as PixelMap); - } else { - imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, value as GIFFrame[]); - } - if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady)) { - // 主图未加载成功,并且未加载失败 显示占位图 主图加载成功或者加载失败后=>不展示占位图 - if (this.thumbHolderFunc != undefined) { - this.thumbHolderFunc?.asyncSuccess(imageKnifeData) - } - } - } - // 缩略图解析失败 - thumbholderOnError = (error?: BusinessError | string) => { - LogUtil.log("缩略图解析失败 error =" + error) - } - // 加载失败 占位图解析成功 - errorholderOnComplete = (imageKnifeData:ImageKnifeData) => { - // 如果有错误占位图 先解析并保存在RequestOption中 等到加载失败时候进行调用 - this.errorholderData = imageKnifeData; - if (this.loadErrorReady) { - if (this.errorholderFunc != undefined) { - this.errorholderFunc.asyncSuccess(imageKnifeData) - } - } - } - // 加载失败 占位图解析失败 - errorholderOnError = (error: BusinessError | string) => { - LogUtil.log("失败占位图解析失败 error =" + error) - } - retryholderOnComplete = (imageKnifeData:ImageKnifeData) => { - this.retryholderData = imageKnifeData; - if (this.loadRetryReady) { - if (this.retryholderFunc != undefined) { - this.retryholderFunc?.asyncSuccess(imageKnifeData) - } - } - } - retryholderOnError = (error: BusinessError | string) => { - LogUtil.log("重试占位图解析失败 error =" + error) - } - //占位图加载失败 后备回调符解析成功 - fallbackOnComplete = (imageKnifeData:ImageKnifeData) => { - this.fallbackData = imageKnifeData; - if (this.loadFallBackReady) { - if (this.fallbackFunc != undefined) { - this.fallbackFunc?.asyncSuccess(imageKnifeData); - } - } - } - // 后备回调符解析失败 - fallbackOnError = (error: BusinessError | string) => { - LogUtil.error("失败占位图解析失败 error =" + JSON.stringify(error)); - } - loadComplete = (imageKnifeData: ImageKnifeData) => { - this.loadMainReady = true; - // 三级缓存数据加载成功 - if (this.requestListeners != undefined) { - for (let i = 0; i < this.requestListeners.length; i++) { - let requestListener = this.requestListeners[i]; - let boolInterception = requestListener.callback("", imageKnifeData); - if (boolInterception) { - break; + let thumbCallback = this.options.thumbholderOnComplete + let thumbError = this.options.thumbholderOnError + this.options.transformations[0].transform(source, thumbOption, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + thumbCallback(pixelMap); + } else { + thumbError(error); } } - } - //输出缓存相关内容和信息 - if (this.allCacheInfoCallback) { - // 内存缓存 - let allCacheInfo: AllCacheInfo = { - memoryCacheInfo: { key: '', data: new ImageKnifeData() }, - resourceCacheInfo: { key: '', path: '' }, - dataCacheInfo: { key: '', path: '' } - }; - allCacheInfo.memoryCacheInfo = { - key: this.generateCacheKey, - data: imageKnifeData - } - let mDiskCacheProxy = new DiskCacheProxy(DiskLruCache.create(ImageKnifeGlobal.getInstance() - .getHapContext() as common.UIAbilityContext)) - // 变换后缓存 - allCacheInfo.resourceCacheInfo = { - key: SparkMD5.hashBinary(this.generateResourceKey) as string, - path: (mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.generateResourceKey)) as string - }; - - // 原图缓存 - allCacheInfo.dataCacheInfo = { - key: SparkMD5.hashBinary(this.generateDataKey) as string, - path: (mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.generateDataKey)) as string - } - this.allCacheInfoCallback.callback(allCacheInfo); - } - - if (imageKnifeData.waitSaveDisk) { - // 等落盘结束后主动调用#removeCurrentAndSearchNext方法 - } else { - // 非落盘情况,直接进行寻找下一个加载 - let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); - if (imageKnife != undefined) { - imageKnife.removeRunning(this); - } - } + }) + setTimeout(() => { + this.options.transformations[0].transform(source, this.options, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); + } else { + onError(error); + } + } + }) + }, this.options.thumbDelayTime) } - // 图片文件落盘之后会自动去寻找下一个数据加载 - removeCurrentAndSearchNext = () => { - if (ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { - (ImageKnifeGlobal.getInstance().getImageKnife())?.removeRunning(this); - } + private svgProcess(option: RequestOption, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void, arraybuffer: ArrayBuffer, typeValue: string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { + let svgParseImpl = new SVGParseImpl() + svgParseImpl.parseSvg(option, arraybuffer, onComplete, onError) } - loadError = (err: BusinessError | string) => { - LogUtil.log("loadError:" + err); - // 失败占位图展示规则 - if (this.retryholderFunc) { - // 重试图层优先于加载失败展示 - this.loadRetryReady = true; - if (this.retryholderData != null) { - this.retryholderFunc.asyncSuccess(this.retryholderData) - } - } else if (!this.retryholderFunc && !this.placeholderFunc && this.fallbackFunc) { - this.loadFallBackReady = true; - if (this.fallbackData != null) { - this.fallbackFunc.asyncSuccess(this.fallbackData); - } - } else { - // 失败图层标记,如果已经有数据直接展示失败图层 - this.loadErrorReady = true; - if (this.errorholderData != null) { - if (this.errorholderFunc != undefined) { - this.errorholderFunc.asyncSuccess(this.errorholderData) - } - } - } - if (this.requestListeners != undefined) { - for (let i = 0; i < this.requestListeners.length; i++) { - let requestListener = this.requestListeners[i]; - let boolInterception = requestListener.callback(err as string, new ImageKnifeData()); - if (boolInterception) { - break; - } + private gifProcess(onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void, arraybuffer: ArrayBuffer, typeValue: string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { + let gifParseImpl = new GIFParseImpl() + gifParseImpl.parseGifs(arraybuffer, (data?: GIFFrame[], err?: BusinessError | string) => { + if (err) { + onError(err) } - } - // 加载失败之后 - if (ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { - (ImageKnifeGlobal.getInstance().getImageKnife())?.removeRunning(this); - } + LogUtil.log("gifProcess data is null:" + (data == null)); + if (!!data) { + // let imageKnifeData = this.createImageGIFFrame(ImageKnifeType.GIFFRAME, data) + // LogUtil.log('gifProcess 生成gif 返回数据类型') + // if (cacheStrategy) { + // LogUtil.log('gifProcess 生成gif并且存入了缓存策略') + // cacheStrategy(imageKnifeData) + // } + onComplete(data) + } else { + onError('Parse GIF callback data is null, you need check callback data!') + } + }) } } - - diff --git a/library/src/main/ets/components/imageknife/SendableData.ets b/library/src/main/ets/components/imageknife/SendableData.ets index 28c1d7c..eb02777 100644 --- a/library/src/main/ets/components/imageknife/SendableData.ets +++ b/library/src/main/ets/components/imageknife/SendableData.ets @@ -41,8 +41,6 @@ export class SendableData{ private diskMemoryCachePath: string = ''; private diskMemoryCache?: DiskLruCache; private dataFetch: IDataFetch = new DownloadClient(); - private placeholderRegisterCacheKey: string = ""; - private placeholderRegisterMemoryCacheKey: string = ""; private downsampType: BaseDownsampling = new DownsampleNone(); public setDataFetch(value: IDataFetch) { this.dataFetch = value; @@ -181,24 +179,8 @@ export class SendableData{ return this.usageType; } - public setPlaceHolderRegisterCacheKey(value: string) { - this.placeholderRegisterCacheKey = value; - } - - public getPlaceHolderRegisterCacheKey(): string { - return this.placeholderRegisterCacheKey; - } - - public setPlaceHolderRegisterMemoryCacheKey(value: string) { - this.placeholderRegisterMemoryCacheKey = value; - } - - public getPlaceHolderRegisterMemoryCacheKey(): string { - return this.placeholderRegisterMemoryCacheKey; - } - - public setDownsampType(Type: BaseDownsampling) { - this.downsampType = Type; + public setDownsampType(Type: BaseDownsampling): BaseDownsampling{ + return this.downsampType = Type; } public getDownsampType(): BaseDownsampling { diff --git a/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets b/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets index 84b7094..74efddd 100644 --- a/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets +++ b/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets @@ -33,7 +33,6 @@ import { GIFFrame } from '../utils/gif/GIFFrame' import { LogUtil } from '../../imageknife/utils/LogUtil' import { BusinessError } from '@ohos.base' import { DataFetchResult } from '../networkmanage/DataFetchResult' -import { RequestOption } from '../RequestOption'; export enum Stage { @@ -167,7 +166,7 @@ export class RequestManager { // gif、webp处理 if ((ImageKnifeData.GIF == typeValue || ImageKnifeData.WEBP == typeValue) && !request.dontAnimateFlag) { // 处理gif、webp - this.gifProcess(onComplete, onError, arrayBuffer, typeValue) + this.gifProcess(onComplete, onError, arrayBuffer, typeValue,request) } else if (ImageKnifeData.SVG == typeValue) { // 处理svg this.svgProcess(request, onComplete, onError, arrayBuffer, typeValue) @@ -188,7 +187,7 @@ export class RequestManager { let success = (value: PixelMap) => { onComplete(value); } - this.mParseImageUtil.parseImage(arrayBuffer, success, onError,request, request) + this.mParseImageUtil.parseImage(arrayBuffer, success, onError,request) } } } @@ -534,9 +533,17 @@ export class RequestManager { svgParseImpl.parseSvg(option, arraybuffer, onComplete, onError) } - private gifProcess(onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void, arraybuffer: ArrayBuffer, typeValue: string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { + private gifProcess( + onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, + onError: (reason?: BusinessError | string) => void, + arraybuffer: ArrayBuffer, + typeValue: string, + request?:RequestOption, + cacheStrategy?: (cacheData: ImageKnifeData) => void + ) { let gifParseImpl = new GIFParseImpl() - gifParseImpl.parseGifs(arraybuffer, (data?: GIFFrame[], err?: BusinessError | string) => { + gifParseImpl.parseGifs( + arraybuffer, (data?: GIFFrame[], err?: BusinessError | string) => { if (err) { onError(err) } @@ -552,6 +559,6 @@ export class RequestManager { } else { onError('Parse GIF callback data is null, you need check callback data!') } - }) + },request) } } diff --git a/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets b/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets index b004153..d6fd0c5 100644 --- a/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets +++ b/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets @@ -43,7 +43,7 @@ export class ParseImageUtil implements IParseImage { editable: true, desiredSize: defaultSize }; - if(request.downsampType.getName()!=='DownsampleNone'){ + if(request?.downsampType.getName()!=='DownsampleNone'){ const b:ESObject = new Downsampler().calculateScaling(imageinfo, hValue, wValue,request) opts= { editable: true, diff --git a/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets b/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets index abe1a37..7039608 100644 --- a/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets +++ b/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets @@ -34,7 +34,11 @@ export interface gifBackData { } export class GIFParseImpl implements IParseGif { - parseGifs(imageinfo: ArrayBuffer, callback: (data?: GIFFrame[], err?: BusinessError | string) => void) { + parseGifs( + imageinfo: ArrayBuffer, + callback: (data?: GIFFrame[], err?: BusinessError | string) => void, + _request?: RequestOption + ) { // 硬解码流程 let imageSource = image.createImageSource(imageinfo); let decodeOpts: image.DecodingOptions = { @@ -42,7 +46,29 @@ export class GIFParseImpl implements IParseGif { editable: true, rotate: 0 } - let data:GIFFrame[] = []; + let hValue: number = 0 + let wValue: number = 0 + imageSource.getImageInfo().then((value) => { + hValue = Math.round(value.size, height); + wValue = Math.round(value.size, height); + console.log('原始宽高:', JSON.stringify(value.size)) + console.log('classType', JSON.stringify(_request?.downsampType.getName())) + console.log('classType2', hValue, wValue) + if (_request?.downsampType.getName() !== 'DownsampleNone') { + const b: ESObject = new Downsampler().calculateScaling(imageinfo, Math.round(value.size.height), Math.round(value.size.width), _request) + console.log("classType1", JSON.stringify(b)) + decodeOpts = { + sampleSize: 1, + editable: true, + rotate: 0, + desiredSize: { + width: b.targetWidth, + height: b.targetHeight + } + } + } + }) + let data: GIFFrame[] = []; imageSource.createPixelMapList(decodeOpts).then((pixelList: Array) => { //sdk的api接口发生变更:从.getDelayTime() 变为.getDelayTimeList() imageSource.getDelayTimeList().then(delayTimes => { @@ -60,20 +86,20 @@ export class GIFParseImpl implements IParseGif { } data.push(frame) } - callback(data,undefined) + callback(data, undefined) imageSource.release(); }).catch((err: string) => { imageSource.release(); - callback(undefined,err) + callback(undefined, err) }) } }).catch((err: string) => { imageSource.release(); - callback(undefined,err) + callback(undefined, err) }) }).catch((err: string) => { imageSource.release(); - callback(undefined,err) + callback(undefined, err) }) } } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/utils/gif/IParseGif.ets b/library/src/main/ets/components/imageknife/utils/gif/IParseGif.ets index d80f388..c83a64d 100644 --- a/library/src/main/ets/components/imageknife/utils/gif/IParseGif.ets +++ b/library/src/main/ets/components/imageknife/utils/gif/IParseGif.ets @@ -17,5 +17,9 @@ import { GIFFrame } from './GIFFrame' import worker from '@ohos.worker'; export interface IParseGif{ // gif解析 - parseGifs:(imageinfo: ArrayBuffer, callback: (data?:GIFFrame[], err?:BusinessError|string) => void, worker?:worker.ThreadWorker,runMainThread?:boolean)=>void + parseGifs:( + imageinfo: ArrayBuffer, + callback: (data?:GIFFrame[], err?:BusinessError|string) => void, + request:ESObject, + worker?:worker.ThreadWorker,runMainThread?:boolean)=>void } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets b/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets index 51f0843..a7c69cf 100644 --- a/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets +++ b/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets @@ -17,6 +17,7 @@ import { RequestOption } from '../../RequestOption' import { BusinessError } from '@ohos.base' import { ImageKnifeData, ImageKnifeType } from '../../ImageKnifeData' import image from '@ohos.multimedia.image' +import { Downsampler } from '../../Downsampling/Downsampler' export class SVGParseImpl implements IParseSvg { parseSvg(option: RequestOption, imageInfo: ArrayBuffer, @@ -24,20 +25,10 @@ export class SVGParseImpl implements IParseSvg { onErrorFunction: (reason?: BusinessError | string) => void) { let imageSource: image.ImageSource = image.createImageSource(imageInfo); // 步骤一:文件转为pixelMap 然后变换 给Image组件 - let hValue = Math.round(option.size.height); - let wValue = Math.round(option.size.width); - let defaultSize: image.Size = { - height: hValue, - width: wValue - }; - let opts: image.DecodingOptions = { - editable: true, - desiredSize: defaultSize - }; - imageSource.getInageInfo().then((value) => { + imageSource.getImageInfo().then((value) => { let hValue = Math.round(value.size.height); let wValue = Math.round(value.size.width); - let defaultSize: image.size = { + let defaultSize: image.Size = { height: hValue, width: wValue }; From 74970631b729e0bd8d1db18ed1b595596ae3d6ec Mon Sep 17 00:00:00 2001 From: sijainguo Date: Mon, 29 Apr 2024 20:15:17 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E9=99=8D=E9=87=87=E6=A0=B7=20=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/imageknife/RequestOption.ets | 1108 +++++++++-------- 1 file changed, 617 insertions(+), 491 deletions(-) diff --git a/library/src/main/ets/components/imageknife/RequestOption.ets b/library/src/main/ets/components/imageknife/RequestOption.ets index 4d4e547..97f6d48 100644 --- a/library/src/main/ets/components/imageknife/RequestOption.ets +++ b/library/src/main/ets/components/imageknife/RequestOption.ets @@ -12,545 +12,671 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { ImageKnife } from './ImageKnife' +import { DiskStrategy } from "../cache/diskstrategy/DiskStrategy" +import { AsyncCallback } from "../imageknife/interface/AsyncCallback" +import { AsyncSuccess } from "../imageknife/interface/AsyncSuccess" +import { AllCacheInfo, IAllCacheInfoCallback } from "../imageknife/interface/IAllCacheInfoCallback" +import { AUTOMATIC } from "../cache/diskstrategy/enum/AUTOMATIC" +import { BaseTransform } from "../imageknife/transform/BaseTransform" +import { RotateImageTransformation } from "../imageknife/transform/RotateImageTransformation" +import { ImageKnifeData, ImageKnifeType } from "../imageknife/ImageKnifeData" +import { CenterCrop } from '../imageknife/transform/pixelmap/CenterCrop' +import { CenterInside } from '../imageknife/transform/pixelmap/CenterInside' +import { FitCenter } from '../imageknife/transform/pixelmap/FitCenter' +import { RoundedCornersTransformation, RoundCorner } from '../imageknife/transform/RoundedCornersTransformation' -import { RequestOption, Size } from '../../imageknife/RequestOption' -import { DiskLruCache } from "../../cache/DiskLruCache" -import { LruCache } from '../../cache/LruCache' -import { SparkMD5 } from '../../3rd_party/sparkmd5/spark-md5' -import { MemoryCacheProxy } from '../requestmanage/MemoryCacheProxy' -import { DiskCacheProxy } from '../requestmanage/DiskCacheProxy' -import { FileTypeUtil } from '../utils/FileTypeUtil' -import { IDataFetch } from '../../imageknife/networkmanage/IDataFetch' -import { IResourceFetch } from '../../imageknife/resourcemanage/IResourceFetch' -import { ImageKnifeData, ImageKnifeType } from '../ImageKnifeData' -import { AllCacheInfo } from '../../imageknife/interface/IAllCacheInfoCallback' -import { ParseImageUtil } from '../utils/ParseImageUtil' -import { IParseImage } from '../interface/IParseImage' -import image from '@ohos.multimedia.image' -import { SVGParseImpl } from '../utils/svg/SVGParseImpl' -import { GIFParseImpl } from '../utils/gif/GIFParseImpl' -import { GIFFrame } from '../utils/gif/GIFFrame' -import { LogUtil } from '../../imageknife/utils/LogUtil' +import { CropCircleTransformation } from '../imageknife/transform/CropCircleTransformation' + +import { + CropCircleWithBorderTransformation, + rgbColor +} from '../imageknife/transform/CropCircleWithBorderTransformation' +import { CropSquareTransformation } from '../imageknife/transform/CropSquareTransformation' +import { CropTransformation } from '../imageknife/transform/CropTransformation' +import { CropType } from '../imageknife/transform/CropTransformation' +import { GrayscaleTransformation } from '../imageknife/transform/GrayscaleTransformation' +import { BrightnessFilterTransformation } from '../imageknife/transform/BrightnessFilterTransformation' +import { ContrastFilterTransformation } from '../imageknife/transform/ContrastFilterTransformation' +import { InvertFilterTransformation } from '../imageknife/transform/InvertFilterTransformation' +import { SepiaFilterTransformation } from '../imageknife/transform/SepiaFilterTransformation' +import { SketchFilterTransformation } from '../imageknife/transform/SketchFilterTransformation' +import { BlurTransformation } from '../imageknife/transform/BlurTransformation' +import { PixelationFilterTransformation } from '../imageknife/transform/PixelationFilterTransformation' +import { MaskTransformation } from '../imageknife/transform/MaskTransformation' +import { SwirlFilterTransformation } from '../imageknife/transform/SwirlFilterTransformation' +import { KuwaharaFilterTransform } from '../imageknife/transform/KuwaharaFilterTransform' +import { ToonFilterTransform } from '../imageknife/transform/ToonFilterTransform' +import { VignetteFilterTransform } from '../imageknife/transform/VignetteFilterTransform' +import { LogUtil } from '../imageknife/utils/LogUtil' +import { ImageKnifeGlobal } from './ImageKnifeGlobal' import { BusinessError } from '@ohos.base' -import { DataFetchResult } from '../networkmanage/DataFetchResult' +import { ObjectKey } from './ObjectKey' +import common from '@ohos.app.ability.common' +import { GIFFrame } from './utils/gif/GIFFrame' +import { DiskCacheProxy } from './requestmanage/DiskCacheProxy' +import { DiskLruCache } from '../cache/DiskLruCache' +import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5' +import { FileUtils } from '../cache/FileUtils' +import util from '@ohos.util' +import { DataFetchResult } from './networkmanage/DataFetchResult' +import { BaseDownsampling } from './Downsampling/BaseDownsampling' -export enum Stage { - - INITIALIZE, - - RESOURCE_CACHE, - - DATA_CACHE, - - SOURCE, - - ENCODE, - - FINISHED, +export interface Size { + width: number, + height: number } - -export enum RunReason { - - INITIALIZE, - - SWITCH_TO_SOURCE_SERVICE, - - DECODE_DATA, +export enum CacheType { + Default, + //缓存 + Cache, + //磁盘 + Disk } -export class RequestManager { - private TAG: string = "RequestManager"; - private options: RequestOption; - private mIDataFetch: IDataFetch; - private mIResourceFetch: IResourceFetch; - private mParseImageUtil: IParseImage; - private diskMemoryCache: DiskLruCache; +export interface DetachFromLayout { + detach: () => void +} - constructor(option: RequestOption,diskMemoryCache: DiskLruCache, dataFetch: IDataFetch, resourceFetch: IResourceFetch) { - this.options = option; +export enum Priority { + HIGH = 0, + MEDIUM = 1, + LOW = 2 +} +export class RequestOption { + // 遍历添加图片http请求头 + headers: Map = new Map(); - this.diskMemoryCache = diskMemoryCache; - // 网络下载能力 - this.mIDataFetch = dataFetch; - - // 本地数据解析能力 - this.mIResourceFetch = resourceFetch; - - // 解析image能力 - this.mParseImageUtil = new ParseImageUtil(); + // RequestOption调用header对于的方法 + addHeader(key: string, value: Object) { + this.headers.set(key, value); } - // DecodeJob work - private mStage: Stage = Stage.INITIALIZE; - private mRunReason: RunReason = RunReason.INITIALIZE; - process = (onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) => { - LogUtil.log("ImageKnife RequestManager process !"); - this.loadLeve1MemoryCache(onComplete, onError) - } - - private runWrapped(request: RequestOption, runReason: RunReason, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - LogUtil.log("ImageKnife RequestManager runWrapped") - if (runReason == RunReason.INITIALIZE) { - this.mStage = this.getNextStage(request, this.mStage); - this.searchLoadFrom(this.options, this.mStage, onComplete, onError); - } else { - throw new Error("Unrecognized run reason: " + runReason) - } - } - - private getNextStage(request: RequestOption, current: Stage): Stage { - if (current == Stage.INITIALIZE) { - return request.strategy.decodeCachedResource() - ? Stage.RESOURCE_CACHE - : this.getNextStage(request, Stage.RESOURCE_CACHE); - } else if (current == Stage.RESOURCE_CACHE) { - return request.strategy.decodeCachedData() - ? Stage.DATA_CACHE - : this.getNextStage(request, Stage.DATA_CACHE); - } else if (current == Stage.DATA_CACHE) { - return request.onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE; - } else if (current == Stage.SOURCE) { - return Stage.FINISHED; - } else if (current == Stage.FINISHED) { - return Stage.FINISHED; - } else { - throw new Error("ImageKnife Unrecognized stage: " + current); - } - } - - // 究竟从哪里加载数据 - private searchLoadFrom(request: RequestOption, current: Stage, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - LogUtil.log("ImageKnife RequestManager searchLoadFrom") - if (current == Stage.RESOURCE_CACHE) { - this.loadDiskFromTransform(request, onComplete, onError); - } else if (current == Stage.DATA_CACHE) { - this.loadDiskFromSource(request, onComplete, onError); - } else if (current == Stage.SOURCE) { - this.parseSource(request, onComplete, onError) - } else if (current == Stage.FINISHED) { - onError("在仅从缓存获取数据中,未获取到数据!") - } else { - throw new Error("Unrecognized stage: " + current); - } - } - - // 加载网络资源 - private async loadSourceFromNetwork(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - try { - LogUtil.log("ImageKnife RequestManager loadSourceFromNetwork") - let result: DataFetchResult = await this.mIDataFetch.loadData(request); - if(result.error){ - onError(result.error) - }else{ - if(result.data){ - this.downloadSuccess(request, result.data, onComplete, onError) - }else{ - onError("datafetch data is null") - } + // 全局调用header对应的方法,包含RequestOption的形式 + addHeaderMap(map: Map) { + map.forEach((value, key) => { + if (!this.headers.has(key)) { + this.addHeader(key, value); } - } catch (e) { - LogUtil.error("ImageKnife RequestManager loadSourceFromNetwork error") - } + }) } - // 加载本地资源 - private loadSourceFormNative(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - LogUtil.log("ImageKnife RequestManager loadSourceFormNative") - // 本地解析后进行一级缓存 - let success = (arrayBuffer: ArrayBuffer) => { - // 使用媒体子系统 ImageSource解析文件 获取PixelMap - let fileTypeUtil = new FileTypeUtil(); - let typeValue = fileTypeUtil.getFileType(arrayBuffer) - LogUtil.log("ImageKnife RequestManager - 文件类型为= " + typeValue) - // gif、webp处理 - if ((ImageKnifeData.GIF == typeValue || ImageKnifeData.WEBP == typeValue) && !request.dontAnimateFlag) { - // 处理gif、webp - this.gifProcess(onComplete, onError, arrayBuffer, typeValue) - } else if (ImageKnifeData.SVG == typeValue) { - // 处理svg - this.svgProcess(request, onComplete, onError, arrayBuffer, typeValue) - } else { - if (request.transformations[0]) { - request.transformations[0].transform(arrayBuffer, request, { - asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { - // 输出给Image - if (pixelMap) { - onComplete(pixelMap); - } else { - onError(error); - } - } - }) - } - else { - let success = (value: PixelMap) => { - onComplete(value); - } - this.mParseImageUtil.parseImage(arrayBuffer, success, onError,request) - } + // 优先级 + priority: Priority = Priority.MEDIUM; + uuid: string = '' // 唯一标识 + loadSrc: string | PixelMap | Resource = ''; + strategy: DiskStrategy = new AUTOMATIC(); + //下采样相关 + downsampType: BaseDownsampling = new BaseDownsamplings() + dontAnimateFlag = false; + placeholderSrc: string | PixelMap | Resource | undefined = undefined; + placeholderFunc: AsyncSuccess | undefined = undefined; + errorholderSrc: PixelMap | Resource | undefined = undefined; + errorholderFunc: AsyncSuccess | undefined = undefined; + errorholderData: ImageKnifeData | undefined = undefined; + thumbSizeMultiplier: number = 0; + // 如果存在缩略图,则主图延时1s加载 + thumbDelayTime: number = 1000 + thumbHolderFunc: AsyncSuccess | undefined = undefined; + requestListeners: Array> | undefined = undefined; + // 进度条 + progressFunc: AsyncSuccess | undefined = undefined; + // 后备回调符 + fallbackSrc: PixelMap | Resource | undefined = undefined; + fallbackFunc: AsyncSuccess | undefined = undefined; + fallbackData: ImageKnifeData | undefined = undefined; + // 重试图层 + retryholderSrc: PixelMap | Resource | undefined = undefined; + retryholderFunc: AsyncSuccess | undefined = undefined; + retryholderData: ImageKnifeData | undefined = undefined; + size: Size = { width: -1, height: -1 }; + // 网络下载数据回调 + allCacheInfoCallback: IAllCacheInfoCallback | undefined = undefined; + onlyRetrieveFromCache: boolean = false; + isCacheable: boolean = true; + // 开启GPU变换绘制 + gpuEnabled: boolean = false; + // 变换相关 + transformations: Array> = new Array(); + generateCacheKey: string = ""; + generateResourceKey: string = ""; + generateDataKey: string = ""; + filesPath: string = ""; // data/data/包名/files目录 + cachesPath: string = ""; // 网络下载默认存储在data/data/包名/cache/ImageKnifeNetworkFolder/目标md5.img下面 + placeholderCacheKey: string = ""; + retryholderCacheKey: string = ""; + errorholderCacheKey: string = ""; + // string类型占位图相关缓存key值 + placeholderRegisterCacheKey: string = ""; + placeholderRegisterMemoryCacheKey: string = ""; + fallbackCacheKey: string = ""; + // 自定义缓存关键字 + signature?: ObjectKey; + // 设置是否使用应用自定义的方式加载图片 + customGetImage?: (context: Context, src: string) => Promise; + // 下载原始文件地址 + downloadFilePath: string = ""; + //磁盘缓存文件路径 + diskMemoryCachePath: string =""; + // 网络文件下载统一存放 + networkCacheFolder: string = "ImageKnifeNetworkFolder" + // 主线图片 状态变化 是否加载完成 + // 主图未加载成功 显示占位图 主图加载成功不展示占位图 + loadMainReady = false; + // 失败占位图展示状态 当true 表示主图加载失败需要展示失败占位图 + loadErrorReady = false; + // 重试占位图展示状态 当true 表示主图加载失败需要展示失败占位图 + loadRetryReady = false; + // 后备回调符展示状态 当true 表占位图加载失败需要展示后备回调符 + loadFallBackReady = false; + // 缩略图展示 + loadThumbnailReady = false; + detachFromLayout: DetachFromLayout = { + detach: () => { + let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + if (imageKnife != undefined) { + imageKnife.remove(this.uuid); } } - let ctx = request.getModuleContext(); + } + // module资源的需要当前的module context + moduleContext?: common.UIAbilityContext = undefined; + + constructor() { + // 初始化全局监听 + this.requestListeners = new Array(); + + let ctx = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext if (ctx != undefined) { - this.mIResourceFetch.loadResource(ctx, request.loadSrc as Resource, success, onError); - } else { - onError('ImageKnife RequestManager loadSourceFormNative moduleContext is undefined! please check it') - } - - } - - // 加载磁盘缓存 原图 - private loadDiskFromSource(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - LogUtil.log("ImageKnife RequestManager loadDiskFromSource") - let cached = this.diskMemoryCache.get(request.generateDataKey); - if (cached != null && cached.byteLength > 0) { - LogUtil.log("ImageKnife loadDiskFromSource load resource from DiskLruCache") - this.parseDiskFile2PixelMap(request, cached, onComplete, onError) - } else { - this.mStage = Stage.SOURCE; - this.mStage = request.onlyRetrieveFromCache? Stage.FINISHED : Stage.SOURCE - this.searchLoadFrom(this.options, this.mStage, onComplete, onError); + this.moduleContext = ctx; } } - - // 加载磁盘缓存 变换后图片 - private loadDiskFromTransform(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - LogUtil.log("ImageKnife RequestManager loadDiskFromTransform") - let cached = this.diskMemoryCache.get(request.generateResourceKey); - if (cached != null) { - LogUtil.log("ImageKnife loadDiskFromTransform load resource from DiskLruCache") - this.parseDiskTransformFile2PixelMap(request, cached, onComplete, onError) - } else { - this.mStage = Stage.DATA_CACHE; - this.searchLoadFrom(this.options, this.mStage, onComplete, onError); - } + setPriority(priority: Priority) { + this.priority = priority + } + setTransformations( array:Array>){ + this.transformations = array; + } + generateUUID(): string { + return SparkMD5.hashBinary(JSON.stringify(this.loadSrc)) as string; } - parseSource(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - LogUtil.log("ImageKnife RequestManager parseSource") - try { - if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') { - // PixelMap 外层捕获效率更高,不会进入这里 - } else if (typeof request.loadSrc == 'string') { - this.loadSourceFromNetwork(request, onComplete, onError); - } else { - let res = request.loadSrc as Resource; - if (typeof res.id != 'undefined') { - this.loadSourceFormNative(request, onComplete, onError) - } else { - LogUtil.log("输入参数有问题!") - } - } - } catch (e) { - LogUtil.error("ImageKnife RequestManager parseSource error") - } - + setModuleContext(moduleCtx: common.UIAbilityContext) { + this.moduleContext = moduleCtx; } - private loadLeve1MemoryCache(onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - try { - this.runWrapped(this.options, this.mRunReason, onComplete, onError) - } catch (e) { - LogUtil.error("ImageKnife loadLeve1MemoryCache error") - } + getModuleContext(): common.UIAbilityContext | undefined { + return this.moduleContext; } - // 解析磁盘文件变成PixeMap - private parseDiskFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - LogUtil.log("ImageKnife RequestManager parseDiskFile2PixelMap") - // 步骤一:文件转为pixelMap 然后变换 给Image组件 - let fileTypeUtil = new FileTypeUtil(); - let typeValue = fileTypeUtil.getFileType(source); - // 解析磁盘文件 gif、webp 和 svg - if ((ImageKnifeData.GIF == typeValue || ImageKnifeData.WEBP == typeValue) && !request.dontAnimateFlag) { - // 处理gif、webp - this.gifProcess(onComplete, onError, source, typeValue) - } else if (ImageKnifeData.SVG == typeValue) { - this.svgProcess(request, onComplete, onError, source, typeValue) - } else { - if (this.options.transformations[0]) { - if (this.options.thumbSizeMultiplier) { - let thumbOption: RequestOption = new RequestOption(); - thumbOption.setImageViewSize({ - width: Math.round(this.options.thumbSizeMultiplier * this.options.size.width), - height: Math.round(this.options.thumbSizeMultiplier * this.options.size.height) - }) - let ctx = this.options.getModuleContext() - if (ctx != undefined) { - thumbOption.setModuleContext(ctx) - } else { - onError('RequestManager parseDiskFile2PixelMap moduleContext is undefined, please check it!') - return - } - let thumbCallback = this.options.thumbholderOnComplete; - let thumbError = this.options.thumbholderOnError; - this.options.transformations[0].transform(source, thumbOption, { - asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { - if (pixelMap) { - thumbCallback(pixelMap); - } else { - thumbError(error); - } - } - }) - setTimeout(() => { - this.options.transformations[0].transform(source, request, { - asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { - if (pixelMap) { - onComplete(pixelMap); - } else { - onError(error); - } - } - }) - }, this.options.thumbDelayTime); - } - else { - this.options.transformations[0].transform(source, request, { - asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { - if (pixelMap) { - onComplete(pixelMap); - } else { - onError(error); - } - } - }) - } - } else { - // thumbnail 缩略图部分 - if (request.thumbSizeMultiplier) { - let thumbCallback = this.options.thumbholderOnComplete - let thumbError = this.options.thumbholderOnError - let thumbSuccess = (value: PixelMap) => { - thumbCallback(value); - } - this.mParseImageUtil.parseImageThumbnail(request.thumbSizeMultiplier, source, thumbSuccess, thumbError); - setTimeout(() => { - let success = (value: PixelMap) => { - onComplete(value); - } - this.mParseImageUtil.parseImage(source, success, onError, request) - }, this.options.thumbDelayTime) - } else { - let success = (value: PixelMap) => { - onComplete(value); - } - this.mParseImageUtil.parseImage(source, success, onError, request) - } - } - } + /** + * set image Component size + */ + setImageViewSize(imageSize: Size) { + this.size.width = imageSize.width; + this.size.height = imageSize.height; + return this; } - // 解析磁盘变换后文件变成PixeMap - private parseDiskTransformFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - LogUtil.log("ImageKnife RequestManager parseDiskTransformFile2PixelMap") - let fileTypeUtil = new FileTypeUtil(); - let typeValue = fileTypeUtil.getFileType(source); - // thumbnail 缩略图部分 - if (request.thumbSizeMultiplier) { - let thumbCallback = this.options.thumbholderOnComplete - let thumbError = this.options.thumbholderOnError - let thumbSuccess = (value: PixelMap) => { - thumbCallback(value); - } - this.mParseImageUtil.parseImageThumbnail(request.thumbSizeMultiplier, source, thumbSuccess, thumbError); - setTimeout(() => { - let success = (value: PixelMap) => { - onComplete(value); - } - this.mParseImageUtil.parseImage(source, success, onError, request) - }, this.options.thumbDelayTime) - } else { - let success = (value: PixelMap) => { - onComplete(value); - } - this.mParseImageUtil.parseImage(source, success, onError, request) - } + getFilesPath() { + return this.filesPath; } - private downloadSuccess(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + setFilesPath(path: string) { + this.filesPath = path; + } - if (source == null || source == undefined || source.byteLength <= 0) { - onError('ImageKnife Download task completed. but download file is empty!') + getCachesPath() { + return this.cachesPath; + } + + setCachesPath(path: string) { + this.cachesPath = path; + } + + load(src: string | PixelMap | Resource) { + this.loadSrc = src; + // 初始化唯一标识,可以用这个标识找到ImageKnife中的对象 + this.uuid = this.generateUUID(); + return this; + } + + diskCacheStrategy(strategy: DiskStrategy) { + this.strategy = strategy; + return this; + } + + dontAnimate() { + this.dontAnimateFlag = true; + return this; + } + + // 仅支持 本地图片 + placeholder(src: string | PixelMap | Resource, func?: AsyncSuccess) { + this.placeholderSrc = src; + this.placeholderFunc = func; + return this; + } + + errorholder(src: PixelMap | Resource, func?: AsyncSuccess) { + this.errorholderSrc = src; + this.errorholderFunc = func; + return this; + } + + fallback(src: PixelMap | Resource, func?: AsyncSuccess) { + this.fallbackSrc = src; + this.fallbackFunc = func; + return this; + } + + retryholder(src: PixelMap | Resource, func?: AsyncSuccess) { + this.retryholderSrc = src; + this.retryholderFunc = func; + return this; + } + + thumbnail(sizeMultiplier: number, func?: AsyncSuccess, displayTime?: number) { + this.thumbSizeMultiplier = sizeMultiplier; + this.thumbHolderFunc = func; + if (displayTime) { + this.thumbDelayTime = displayTime; + } + return this; + } + + addProgressListener(func?: AsyncSuccess) { + this.progressFunc = func; + return this; + } + + addListener(func: AsyncCallback) { + if (this.requestListeners != undefined) { + this.requestListeners?.push(func); + } + return this; + } + + addAllCacheInfoCallback(func: IAllCacheInfoCallback) { + this.allCacheInfoCallback = func; + return this; + } + + skipMemoryCache(skip: boolean) { + this.isCacheable = !skip; + return this; + } + + retrieveDataFromCache(flag: boolean) { + this.onlyRetrieveFromCache = flag; + } + + rotateImage(degreesToRotate: number | undefined) { + if (degreesToRotate == undefined) { return } - - // 下载成功之后 去data/data/包名/唯一路径/文件名 读取数据 - // 步骤一:文件转为pixelMap 然后变换 给Image组件 - // 步骤二: 文件名保存一份全局 - // 步骤三:查看文件是否支持 非支持类型直接返回 - let fileTypeUtil = new FileTypeUtil(); - let filetype: string | null = fileTypeUtil.getFileType(source); - if (filetype == null) { - onError("下载文件解析后类型为null,请检查数据源!"); - return; - } - - if (!fileTypeUtil.isImage(source)) { - onError("暂不支持 下载文件类型!类型=" + filetype); - return; - } - - // 解析磁盘文件 gif、webp 和 svg - if ((ImageKnifeData.GIF == filetype || ImageKnifeData.WEBP == filetype) && !this.options.dontAnimateFlag) { - // 处理gif、webp - this.gifProcess(onComplete, onError, source, filetype) - - // 保存二级磁盘缓存 - Promise.resolve(source) - .then((arraybuffer: ArrayBuffer) => { - this.diskMemoryCache.set(this.options.generateDataKey,arraybuffer); - }) - .catch((err: BusinessError) => { - LogUtil.log('download file is =' + ImageKnifeData.GIF + 'and save diskLruCache error =' + (err as BusinessError)) - }) - } else if (ImageKnifeData.SVG == filetype) { - // 处理svg - this.svgProcess(request, onComplete, onError, source, filetype) - - // 保存二级磁盘缓存 - Promise.resolve(source) - .then((arraybuffer: ArrayBuffer) => { - this.diskMemoryCache.set(this.options.generateDataKey,arraybuffer); - }) - .catch((err: BusinessError) => { - LogUtil.log('download file is =' + ImageKnifeData.SVG + 'and save diskLruCache error =' + (err as BusinessError)) - }) - } else { - // 进行变换 - if (this.options.transformations[0]) { - // thumbnail 缩略图部分 - if (this.options.thumbSizeMultiplier) { - if (filetype != null) { - this.thumbnailProcess(source, filetype, onComplete, onError); - } - } else { - this.options.transformations[0].transform(source, this.options, { - asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { - if (pixelMap) { - if (filetype != null) { - this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); - } - } else { - onError(error); - } - } - }) - } - } else { - // thumbnail 缩略图部分 - if (this.options.thumbSizeMultiplier) { - let thumbCallback = this.options.thumbholderOnComplete - let thumbError = this.options.thumbholderOnError - let thumbSuccess = (value: PixelMap) => { - thumbCallback(value); - } - this.mParseImageUtil.parseImageThumbnail(this.options.thumbSizeMultiplier, source, thumbSuccess, thumbError); - setTimeout(() => { - let success = (value: PixelMap) => { - if (filetype != null) { - this.saveCacheAndDisk(value, filetype, onComplete, source); - } - } - this.mParseImageUtil.parseImage(source, success, onError, request) - }, this.options.thumbDelayTime) - } else { - let success = (value: PixelMap) => { - if (filetype != null) { - this.saveCacheAndDisk(value, filetype, onComplete, source); - } - } - this.mParseImageUtil.parseImage(source, success, onError, request) - } - } - } + let rotateImage = new RotateImageTransformation(degreesToRotate); + this.transformations.push(rotateImage); + return this; } - createImagePixelMap(imageKnifeType: ImageKnifeType, imageKnifeValue: PixelMap): ImageKnifeData { - return ImageKnifeData.createImagePixelMap(imageKnifeType, imageKnifeValue); + centerCrop() { + this.transformations.push(new CenterCrop()); + return this; } - createImageGIFFrame(imageKnifeType: ImageKnifeType, imageKnifeValue: GIFFrame[]): ImageKnifeData { - return ImageKnifeData.createImageGIFFrame(imageKnifeType, imageKnifeValue); + centerInside() { + this.transformations.push(new CenterInside()); + return this; } - - private async saveCacheAndDisk(value: PixelMap, filetype: string, onComplete: (value: PixelMap) => void | PromiseLike, source: ArrayBuffer) { - let save2DiskCache = (arraybuffer: ArrayBuffer) => { - this.diskMemoryCache.set(this.options.generateDataKey,arraybuffer); - // 落盘之后需要主动移除当前request并且调用下一个加载 - let removeCurrentAndSearchNextRun = this.options.removeCurrentAndSearchNext; - removeCurrentAndSearchNextRun(); - } - let runSave2Disk = (resolve: (value: ArrayBuffer) => void | PromiseLike, reject: (reason?: BusinessError | string) => void) => { - resolve(source); - } - let promise = new Promise(runSave2Disk); - await promise.then(save2DiskCache); - onComplete(value); + fitCenter() { + this.transformations.push(new FitCenter()); + return this; } - thumbnailProcess(source: ArrayBuffer, filetype: string, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - let thumbOption = new RequestOption(); - let ctx = this.options.getModuleContext() - if (ctx != undefined) { - thumbOption.setModuleContext(ctx) - } else { - onError('RequestManager thumbnailProcess moduleContext is undefined, please check it!') + roundedCorners(obj: RoundCorner | undefined) { + if (obj == undefined) { return } - thumbOption.setImageViewSize({ - width: Math.round(this.options.thumbSizeMultiplier * this.options.size.width), - height: Math.round(this.options.thumbSizeMultiplier * this.options.size.height) + let transformation = new RoundedCornersTransformation({ + top_left: obj.top_left, + top_right: obj.top_right, + bottom_left: obj.bottom_left, + bottom_right: obj.bottom_right }) - let thumbCallback = this.options.thumbholderOnComplete - let thumbError = this.options.thumbholderOnError - this.options.transformations[0].transform(source, thumbOption, { - asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { - if (pixelMap) { - thumbCallback(pixelMap); - } else { - thumbError(error); - } - } - }) - setTimeout(() => { - this.options.transformations[0].transform(source, this.options, { - asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { - if (pixelMap) { - this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); - } else { - onError(error); - } - } - }) - }, this.options.thumbDelayTime) + this.transformations.push(transformation); + return this; } - private svgProcess(option: RequestOption, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void, arraybuffer: ArrayBuffer, typeValue: string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { - let svgParseImpl = new SVGParseImpl() - svgParseImpl.parseSvg(option, arraybuffer, onComplete, onError) + cropCircle() { + let transformation = new CropCircleTransformation() + this.transformations.push(transformation); + return this; } - private gifProcess(onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void, arraybuffer: ArrayBuffer, typeValue: string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { - let gifParseImpl = new GIFParseImpl() - gifParseImpl.parseGifs(arraybuffer, (data?: GIFFrame[], err?: BusinessError | string) => { - if (err) { - onError(err) + cropCircleWithBorder(border: number | undefined, obj: rgbColor | undefined) { + if (border == undefined || obj == undefined) { + return + } + let transformation = new CropCircleWithBorderTransformation(border, obj) + this.transformations.push(transformation); + return this; + } + + cropSquare() { + let transformation = new CropSquareTransformation() + this.transformations.push(transformation); + return this; + } + + crop(width: number | undefined, height: number | undefined, cropType: CropType | undefined) { + if (width == undefined || height == undefined || cropType == undefined) { + return + } + let transformation = new CropTransformation(width, height, cropType) + this.transformations.push(transformation); + return this; + } + + grayscale() { + let transformation = new GrayscaleTransformation() + this.transformations.push(transformation); + return this; + } + + brightnessFilter(brightness: number | undefined) { + if (brightness == undefined) { + return + } + let transformation = new BrightnessFilterTransformation(brightness) + this.transformations.push(transformation); + return this; + } + + contrastFilter(contrast: number | undefined) { + if (contrast == undefined) { + return + } + let transformation = new ContrastFilterTransformation(contrast) + this.transformations.push(transformation); + return this; + } + + invertFilter() { + let transformation = new InvertFilterTransformation() + this.transformations.push(transformation); + return this; + } + + sepiaFilter() { + let transformation = new SepiaFilterTransformation() + this.transformations.push(transformation); + return this; + } + + sketchFilter() { + let transformation = new SketchFilterTransformation() + this.transformations.push(transformation); + return this; + } + + blur(radius: number | undefined, sampling?: number) { + if (radius == undefined) { + return + } + if (sampling == undefined) { + let transformation = new BlurTransformation(radius) + this.transformations.push(transformation); + } else { + let transformation = new BlurTransformation(radius, sampling) + this.transformations.push(transformation); + } + return this; + } + + pixelationFilter(pixel: number | undefined) { + if (pixel == undefined) { + return + } + let transformation = new PixelationFilterTransformation(pixel) + this.transformations.push(transformation); + return this; + } + + swirlFilter(degree: number | undefined) { + if (degree == undefined) { + return + } + let transformation = new SwirlFilterTransformation(degree) + this.transformations.push(transformation); + return this; + } + + mask(maskResource: Resource | undefined) { + if (maskResource == undefined) { + return + } + let transformation = new MaskTransformation(maskResource) + this.transformations.push(transformation); + return this; + } + + kuwaharaFilter(radius: number | undefined) { + if (radius == undefined) { + return + } + let transformation = new KuwaharaFilterTransform(radius); + this.transformations.push(transformation); + return this; + } + + toonFilter(threshold: number | undefined, quantizationLevels: number | undefined) { + if (threshold == undefined || quantizationLevels == undefined) { + return + } + let transformation = new ToonFilterTransform(threshold, quantizationLevels); + this.transformations.push(transformation); + return this; + } + + vignetteFilter(centerPoint: Array | undefined, vignetteColor: Array | undefined, vignetteSpace: Array | undefined) { + if (centerPoint == undefined || vignetteColor == undefined || vignetteSpace == undefined) { + return + } + let transformation = new VignetteFilterTransform(centerPoint, vignetteColor, vignetteSpace); + this.transformations.push(transformation); + return this; + } + + transform(input: BaseTransform) { + this.transformations.push(input); + return this; + } + + transforms(inputs: BaseTransform[]) { + this.transformations = inputs; + return this; + } + + // 开启GPU变换绘制 + enableGPU() { + this.gpuEnabled = true; + return this; + } + downsampleStrategy(downsampType: ESObject){ + this.downsampType = downsampType + return this; + } + + // 占位图解析成功 + placeholderOnComplete = (imageKnifeData:ImageKnifeData) => { + LogUtil.log("placeholderOnComplete has called!"); + LogUtil.log("Main Image is Ready:" + this.loadMainReady); + if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady) && !this.loadThumbnailReady) { + // 主图未加载成功,并且未加载失败 显示占位图 主图加载成功或者加载失败后=>不展示占位图 + if (this.placeholderSrc != undefined) { + this.placeholderFunc?.asyncSuccess(imageKnifeData) } - LogUtil.log("gifProcess data is null:" + (data == null)); - if (!!data) { - // let imageKnifeData = this.createImageGIFFrame(ImageKnifeType.GIFFRAME, data) - // LogUtil.log('gifProcess 生成gif 返回数据类型') - // if (cacheStrategy) { - // LogUtil.log('gifProcess 生成gif并且存入了缓存策略') - // cacheStrategy(imageKnifeData) - // } - onComplete(data) - } else { - onError('Parse GIF callback data is null, you need check callback data!') + } + } + // 占位图解析失败 + placeholderOnError = (error: BusinessError | string) => { + LogUtil.log("占位图解析失败 error =" + error) + } + // 缩略图解析成功 + thumbholderOnComplete = (value: PixelMap | GIFFrame[]) => { + let imageKnifeData = new ImageKnifeData(); + if ((typeof (value as PixelMap).isEditable) == 'boolean') { + imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value as PixelMap); + } else { + imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, value as GIFFrame[]); + } + if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady)) { + // 主图未加载成功,并且未加载失败 显示占位图 主图加载成功或者加载失败后=>不展示占位图 + if (this.thumbHolderFunc != undefined) { + this.thumbHolderFunc?.asyncSuccess(imageKnifeData) } - }) + } + } + // 缩略图解析失败 + thumbholderOnError = (error?: BusinessError | string) => { + LogUtil.log("缩略图解析失败 error =" + error) + } + // 加载失败 占位图解析成功 + errorholderOnComplete = (imageKnifeData:ImageKnifeData) => { + // 如果有错误占位图 先解析并保存在RequestOption中 等到加载失败时候进行调用 + this.errorholderData = imageKnifeData; + if (this.loadErrorReady) { + if (this.errorholderFunc != undefined) { + this.errorholderFunc.asyncSuccess(imageKnifeData) + } + } + } + // 加载失败 占位图解析失败 + errorholderOnError = (error: BusinessError | string) => { + LogUtil.log("失败占位图解析失败 error =" + error) + } + retryholderOnComplete = (imageKnifeData:ImageKnifeData) => { + this.retryholderData = imageKnifeData; + if (this.loadRetryReady) { + if (this.retryholderFunc != undefined) { + this.retryholderFunc?.asyncSuccess(imageKnifeData) + } + } + } + retryholderOnError = (error: BusinessError | string) => { + LogUtil.log("重试占位图解析失败 error =" + error) + } + //占位图加载失败 后备回调符解析成功 + fallbackOnComplete = (imageKnifeData:ImageKnifeData) => { + this.fallbackData = imageKnifeData; + if (this.loadFallBackReady) { + if (this.fallbackFunc != undefined) { + this.fallbackFunc?.asyncSuccess(imageKnifeData); + } + } + } + // 后备回调符解析失败 + fallbackOnError = (error: BusinessError | string) => { + LogUtil.error("失败占位图解析失败 error =" + JSON.stringify(error)); + } + loadComplete = (imageKnifeData: ImageKnifeData) => { + this.loadMainReady = true; + // 三级缓存数据加载成功 + if (this.requestListeners != undefined) { + for (let i = 0; i < this.requestListeners.length; i++) { + let requestListener = this.requestListeners[i]; + let boolInterception = requestListener.callback("", imageKnifeData); + if (boolInterception) { + break; + } + } + } + //输出缓存相关内容和信息 + if (this.allCacheInfoCallback) { + // 内存缓存 + let allCacheInfo: AllCacheInfo = { + memoryCacheInfo: { key: '', data: new ImageKnifeData() }, + resourceCacheInfo: { key: '', path: '' }, + dataCacheInfo: { key: '', path: '' } + }; + allCacheInfo.memoryCacheInfo = { + key: this.generateCacheKey, + data: imageKnifeData + } + let mDiskCacheProxy = new DiskCacheProxy(DiskLruCache.create(ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext)) + // 变换后缓存 + allCacheInfo.resourceCacheInfo = { + key: SparkMD5.hashBinary(this.generateResourceKey) as string, + path: (mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.generateResourceKey)) as string + }; + + // 原图缓存 + allCacheInfo.dataCacheInfo = { + key: SparkMD5.hashBinary(this.generateDataKey) as string, + path: (mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.generateDataKey)) as string + } + this.allCacheInfoCallback.callback(allCacheInfo); + } + + if (imageKnifeData.waitSaveDisk) { + // 等落盘结束后主动调用#removeCurrentAndSearchNext方法 + } else { + // 非落盘情况,直接进行寻找下一个加载 + let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + if (imageKnife != undefined) { + imageKnife.removeRunning(this); + } + } + } + + // 图片文件落盘之后会自动去寻找下一个数据加载 + removeCurrentAndSearchNext = () => { + if (ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { + (ImageKnifeGlobal.getInstance().getImageKnife())?.removeRunning(this); + } + } + loadError = (err: BusinessError | string) => { + LogUtil.log("loadError:" + err); + // 失败占位图展示规则 + if (this.retryholderFunc) { + // 重试图层优先于加载失败展示 + this.loadRetryReady = true; + if (this.retryholderData != null) { + this.retryholderFunc.asyncSuccess(this.retryholderData) + } + } else if (!this.retryholderFunc && !this.placeholderFunc && this.fallbackFunc) { + this.loadFallBackReady = true; + if (this.fallbackData != null) { + this.fallbackFunc.asyncSuccess(this.fallbackData); + } + } else { + // 失败图层标记,如果已经有数据直接展示失败图层 + this.loadErrorReady = true; + if (this.errorholderData != null) { + if (this.errorholderFunc != undefined) { + this.errorholderFunc.asyncSuccess(this.errorholderData) + } + } + } + + if (this.requestListeners != undefined) { + for (let i = 0; i < this.requestListeners.length; i++) { + let requestListener = this.requestListeners[i]; + let boolInterception = requestListener.callback(err as string, new ImageKnifeData()); + if (boolInterception) { + break; + } + } + } + // 加载失败之后 + if (ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { + (ImageKnifeGlobal.getInstance().getImageKnife())?.removeRunning(this); + } } } + +