parent
bcce9177bc
commit
38144acd93
|
@ -34,9 +34,11 @@ struct DownSamplePage {
|
||||||
@Local SamplingList: SamplingType[] = [
|
@Local SamplingList: SamplingType[] = [
|
||||||
new SamplingType(0, "NONE"),
|
new SamplingType(0, "NONE"),
|
||||||
new SamplingType(1, "AT_MOST"),
|
new SamplingType(1, "AT_MOST"),
|
||||||
new SamplingType(2, "FIT_CENTER"),
|
new SamplingType(2, "FIT_CENTER_MEMORY"),
|
||||||
new SamplingType(3, "CENTER_INSIDE"),
|
new SamplingType(4, "FIT_CENTER_QUALITY"),
|
||||||
new SamplingType(4, "CENTER_OUTSIDE"),
|
new SamplingType(5, "CENTER_INSIDE_MEMORY"),
|
||||||
|
new SamplingType(6, "CENTER_INSIDE_QUALITY"),
|
||||||
|
new SamplingType(7, "CENTER_OUTSIDE"),
|
||||||
]
|
]
|
||||||
@Local checked: boolean = false
|
@Local checked: boolean = false
|
||||||
|
|
||||||
|
@ -61,27 +63,50 @@ struct DownSamplePage {
|
||||||
})
|
})
|
||||||
this.originalPixMap($r('app.media.svgSample'))
|
this.originalPixMap($r('app.media.svgSample'))
|
||||||
this.afterSamplingFunc($r('app.media.svgSample'))
|
this.afterSamplingFunc($r('app.media.svgSample'))
|
||||||
} else if (value === 'FIT_CENTER') {
|
} else if (value === 'FIT_CENTER_MEMORY') {
|
||||||
this.imageKnifeOption =new ImageKnifeOption( {
|
this.imageKnifeOption =new ImageKnifeOption( {
|
||||||
loadSrc: $r('app.media.pngSample'),
|
loadSrc: $r('app.media.pngSample'),
|
||||||
placeholderSrc: $r("app.media.loading"),
|
placeholderSrc: $r("app.media.loading"),
|
||||||
errorholderSrc: $r("app.media.app_icon"),
|
errorholderSrc: $r("app.media.app_icon"),
|
||||||
objectFit: ImageFit.Contain,
|
objectFit: ImageFit.Contain,
|
||||||
downsampleOf: DownsampleStrategy.FIT_CENTER
|
downsampleOf: DownsampleStrategy.FIT_CENTER_MEMORY
|
||||||
})
|
})
|
||||||
this.originalPixMap($r('app.media.pngSample'))
|
this.originalPixMap($r('app.media.pngSample'))
|
||||||
this.afterSamplingFunc($r('app.media.pngSample'))
|
this.afterSamplingFunc($r('app.media.pngSample'))
|
||||||
} else if (value === 'CENTER_INSIDE') {
|
}
|
||||||
|
else if (value ==='FIT_CENTER_QUALITY') {
|
||||||
|
this.imageKnifeOption =new ImageKnifeOption( {
|
||||||
|
loadSrc: $r('app.media.pngSample'),
|
||||||
|
placeholderSrc: $r("app.media.loading"),
|
||||||
|
errorholderSrc: $r("app.media.app_icon"),
|
||||||
|
objectFit: ImageFit.Contain,
|
||||||
|
downsampleOf: DownsampleStrategy.FIT_CENTER_QUALITY
|
||||||
|
})
|
||||||
|
this.originalPixMap($r('app.media.pngSample'))
|
||||||
|
this.afterSamplingFunc($r('app.media.pngSample'))
|
||||||
|
}
|
||||||
|
else if (value === 'CENTER_INSIDE_MEMORY') {
|
||||||
this.imageKnifeOption = new ImageKnifeOption({
|
this.imageKnifeOption = new ImageKnifeOption({
|
||||||
loadSrc: $r('app.media.jpgSample1'),
|
loadSrc: $r('app.media.jpgSample1'),
|
||||||
placeholderSrc: $r("app.media.loading"),
|
placeholderSrc: $r("app.media.loading"),
|
||||||
errorholderSrc: $r("app.media.app_icon"),
|
errorholderSrc: $r("app.media.app_icon"),
|
||||||
objectFit: ImageFit.Contain,
|
objectFit: ImageFit.Contain,
|
||||||
downsampleOf: DownsampleStrategy.CENTER_INSIDE
|
downsampleOf: DownsampleStrategy.CENTER_INSIDE_MEMORY
|
||||||
})
|
})
|
||||||
this.originalPixMap($r('app.media.jpgSample1'))
|
this.originalPixMap($r('app.media.jpgSample1'))
|
||||||
this.afterSamplingFunc($r('app.media.jpgSample1'))
|
this.afterSamplingFunc($r('app.media.jpgSample1'))
|
||||||
} else {
|
} else if (value === 'CENTER_INSIDE_QUALITY') {
|
||||||
|
this.imageKnifeOption = new ImageKnifeOption({
|
||||||
|
loadSrc: $r('app.media.jpgSample1'),
|
||||||
|
placeholderSrc: $r("app.media.loading"),
|
||||||
|
errorholderSrc: $r("app.media.app_icon"),
|
||||||
|
objectFit: ImageFit.Contain,
|
||||||
|
downsampleOf: DownsampleStrategy.CENTER_INSIDE_QUALITY
|
||||||
|
})
|
||||||
|
this.originalPixMap($r('app.media.jpgSample1'))
|
||||||
|
this.afterSamplingFunc($r('app.media.jpgSample1'))
|
||||||
|
}
|
||||||
|
else {
|
||||||
this.imageKnifeOption = new ImageKnifeOption({
|
this.imageKnifeOption = new ImageKnifeOption({
|
||||||
loadSrc: $r('app.media.jpgSample2'),
|
loadSrc: $r('app.media.jpgSample2'),
|
||||||
placeholderSrc: $r("app.media.loading"),
|
placeholderSrc: $r("app.media.loading"),
|
||||||
|
@ -106,7 +131,7 @@ struct DownSamplePage {
|
||||||
let imageInfo = await imageSource.getImageInfo()
|
let imageInfo = await imageSource.getImageInfo()
|
||||||
let reqSize =
|
let reqSize =
|
||||||
new Downsampler().calculateScaling(typeValue, imageInfo.size.width, imageInfo.size.height, 300,
|
new Downsampler().calculateScaling(typeValue, imageInfo.size.width, imageInfo.size.height, 300,
|
||||||
300, this.imageKnifeOption.downsampleOf, false)
|
300, this.imageKnifeOption.downsampleOf)
|
||||||
decodingOptions = {
|
decodingOptions = {
|
||||||
editable: true,
|
editable: true,
|
||||||
desiredSize: {
|
desiredSize: {
|
||||||
|
|
|
@ -40,6 +40,8 @@ import {
|
||||||
} from './model/ImageKnifeData'
|
} from './model/ImageKnifeData'
|
||||||
import { combineArrayBuffers } from './model/utils';
|
import { combineArrayBuffers } from './model/utils';
|
||||||
import { BusinessError } from '@kit.BasicServicesKit';
|
import { BusinessError } from '@kit.BasicServicesKit';
|
||||||
|
import { Downsampler } from './downsampling/Downsampler';
|
||||||
|
import { DownsampleStrategy } from './downsampling/DownsampleStartegy';
|
||||||
|
|
||||||
export class ImageKnifeDispatcher {
|
export class ImageKnifeDispatcher {
|
||||||
// 最大并发
|
// 最大并发
|
||||||
|
@ -659,7 +661,7 @@ async function requestJob(request: RequestJobRequest, requestList?: List<ImageKn
|
||||||
request.requestSource == ImageKnifeRequestSource.SRC) {
|
request.requestSource == ImageKnifeRequestSource.SRC) {
|
||||||
let reqSize =
|
let reqSize =
|
||||||
new Downsampler().calculateScaling(typeValue, imageInfo.size.width, imageInfo.size.height, request.targetWidth,
|
new Downsampler().calculateScaling(typeValue, imageInfo.size.width, imageInfo.size.height, request.targetWidth,
|
||||||
request.targetHeight, request.downsampType, autoResizes)
|
request.targetHeight, request.downsampType)
|
||||||
opts = {
|
opts = {
|
||||||
editable: true,
|
editable: true,
|
||||||
desiredSize: {
|
desiredSize: {
|
||||||
|
@ -686,7 +688,7 @@ async function requestJob(request: RequestJobRequest, requestList?: List<ImageKn
|
||||||
request.requestSource == ImageKnifeRequestSource.SRC) {
|
request.requestSource == ImageKnifeRequestSource.SRC) {
|
||||||
let reqSize =
|
let reqSize =
|
||||||
new Downsampler().calculateScaling(typeValue, imageInfo.size.width, imageInfo.size.height, request.targetWidth,
|
new Downsampler().calculateScaling(typeValue, imageInfo.size.width, imageInfo.size.height, request.targetWidth,
|
||||||
request.targetHeight, request.downsampType, autoResizes)
|
request.targetHeight, request.downsampType)
|
||||||
|
|
||||||
decodingOptions = {
|
decodingOptions = {
|
||||||
editable: true,
|
editable: true,
|
||||||
|
|
|
@ -12,14 +12,15 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
import { DownsampleStrategy } from './DownsampleStartegy';
|
||||||
|
|
||||||
import { SampleSizeRounding } from './DownsampleUtils';
|
import { SampleSizeRounding } from './DownsampleUtils';
|
||||||
|
|
||||||
export interface BaseDownsampling {
|
export interface BaseDownsampling {
|
||||||
getName(): string
|
getName(): string
|
||||||
|
|
||||||
getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number
|
getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number,downsampType?:DownsampleStrategy): number
|
||||||
|
|
||||||
getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number,
|
getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number,
|
||||||
requestHeight: number): SampleSizeRounding
|
requestHeight: number,downsampType?:DownsampleStrategy): SampleSizeRounding
|
||||||
}
|
}
|
|
@ -20,16 +20,13 @@ export class FitCenter implements BaseDownsampling {
|
||||||
return "FitCenter"
|
return "FitCenter"
|
||||||
}
|
}
|
||||||
|
|
||||||
//实现 getScaleFactor 方法
|
getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy
|
||||||
getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
|
||||||
): number {
|
): number {
|
||||||
const IS_BITMAP_FACTORY_SCALING_SUPPORTED = autoResize;
|
if (downsampType===DownsampleStrategy.FIT_CENTER_QUALITY) {
|
||||||
if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) {
|
|
||||||
const widthPercentage = requestedWidth / sourceWidth
|
const widthPercentage = requestedWidth / sourceWidth
|
||||||
const heightPercentage = requestedHeight / sourceHeight
|
const heightPercentage = requestedHeight / sourceHeight
|
||||||
return Math.min(widthPercentage, heightPercentage)
|
return Math.min(widthPercentage, heightPercentage)
|
||||||
} else {
|
} else {
|
||||||
//类似 AT_LEAST,但只要求一个维度或另一个维度大于等于请求的尺寸
|
|
||||||
const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth);
|
const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth);
|
||||||
const a = maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor)
|
const a = maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor)
|
||||||
return a;
|
return a;
|
||||||
|
@ -37,11 +34,9 @@ export class FitCenter implements BaseDownsampling {
|
||||||
}
|
}
|
||||||
|
|
||||||
//实现 getSampleSizeRounding 方法
|
//实现 getSampleSizeRounding 方法
|
||||||
getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy
|
||||||
): number {
|
): number {
|
||||||
const IS_BITMAP_FACTORY_SCALING_SUPPORTED = autoResize;
|
if (downsampType===DownsampleStrategy.FIT_CENTER_QUALITY) {
|
||||||
|
|
||||||
if (!IS_BITMAP_FACTORY_SCALING_SUPPORTED) {
|
|
||||||
return SampleSizeRounding.QUALITY;
|
return SampleSizeRounding.QUALITY;
|
||||||
} else {
|
} else {
|
||||||
return SampleSizeRounding.MEMORY;
|
return SampleSizeRounding.MEMORY;
|
||||||
|
@ -127,20 +122,19 @@ export class CenterInside implements BaseDownsampling {
|
||||||
return "CenterInside"
|
return "CenterInside"
|
||||||
}
|
}
|
||||||
|
|
||||||
getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy
|
||||||
): number {
|
): number {
|
||||||
//获取 FIT_CENTER 的缩放因子
|
//获取 FIT_CENTER 的缩放因子
|
||||||
const fitCenterScaleFactor: ESObject =
|
const fitCenterScaleFactor: ESObject =
|
||||||
this.getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, autoResize);
|
this.getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType);
|
||||||
//返回不超过 1 的缩放因子,即尽量缩小图像以适应目标尺寸,但不会放大
|
//返回不超过 1 的缩放因子,即尽量缩小图像以适应目标尺寸,但不会放大
|
||||||
return Math.min(1, fitCenterScaleFactor);
|
return Math.min(1, fitCenterScaleFactor);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getScale(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
getScale(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy
|
||||||
): number {
|
): number {
|
||||||
const IS_BITMAP_FACTORY_SCALING_SUPPORTED = autoResize;
|
if (downsampType===DownsampleStrategy.FIT_CENTER_QUALITY) {
|
||||||
if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) {
|
|
||||||
const widthPercentage = requestedWidth / sourceWidth
|
const widthPercentage = requestedWidth / sourceWidth
|
||||||
const heightPercentage = requestedHeight / sourceHeight
|
const heightPercentage = requestedHeight / sourceHeight
|
||||||
return Math.min(widthPercentage, heightPercentage)
|
return Math.min(widthPercentage, heightPercentage)
|
||||||
|
@ -152,20 +146,19 @@ export class CenterInside implements BaseDownsampling {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy
|
||||||
): SampleSizeRounding {
|
): SampleSizeRounding {
|
||||||
//如果缩放因子为 1,表示没有缩放,优先选择质量
|
//如果缩放因子为 1,表示没有缩放,优先选择质量
|
||||||
if (this.getScaleFactor(sourceWidth, sourceHeight, requestedWidth, requestedHeight, autoResize) === 1) {
|
if (this.getScaleFactor(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType) === 1) {
|
||||||
return SampleSizeRounding.QUALITY
|
return SampleSizeRounding.QUALITY
|
||||||
}
|
}
|
||||||
//否则,使用 FIL_CENTER 的 SampleSizeRounding 值
|
//否则,使用 FIL_CENTER 的 SampleSizeRounding 值
|
||||||
return this.getSampleSize(sourceWidth, sourceHeight, requestedWidth, requestedHeight);
|
return this.getSampleSize(sourceWidth, sourceHeight, requestedWidth, requestedHeight,downsampType);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSampleSize(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
getSampleSize(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy
|
||||||
): SampleSizeRounding {
|
): SampleSizeRounding {
|
||||||
const IS_BITMAP_FACTORY_SCALING_SUPPORTED = autoResize;
|
if (downsampType===DownsampleStrategy.CENTER_INSIDE_QUALITY) {
|
||||||
if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) {
|
|
||||||
return SampleSizeRounding.QUALITY;
|
return SampleSizeRounding.QUALITY;
|
||||||
} else {
|
} else {
|
||||||
return SampleSizeRounding.MEMORY;
|
return SampleSizeRounding.MEMORY;
|
||||||
|
|
|
@ -70,11 +70,11 @@ export class Downsampler {
|
||||||
let exactScaleFactor: number | undefined
|
let exactScaleFactor: number | undefined
|
||||||
let rounding: SampleSizeRounding | undefined
|
let rounding: SampleSizeRounding | undefined
|
||||||
switch (downsampType) {
|
switch (downsampType) {
|
||||||
case DownsampleStrategy.FIT_CENTER:
|
case DownsampleStrategy.FIT_CENTER_MEMORY || DownsampleStrategy.FIT_CENTER_QUALITY:
|
||||||
exactScaleFactor = new FitCenter()
|
exactScaleFactor = new FitCenter()
|
||||||
.getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight)
|
.getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight,downsampType)
|
||||||
rounding = new FitCenter()
|
rounding = new FitCenter()
|
||||||
.getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight)
|
.getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight,downsampType)
|
||||||
break;
|
break;
|
||||||
case DownsampleStrategy.NONE:
|
case DownsampleStrategy.NONE:
|
||||||
exactScaleFactor = new None()
|
exactScaleFactor = new None()
|
||||||
|
@ -89,10 +89,11 @@ export class Downsampler {
|
||||||
.getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight)
|
.getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight)
|
||||||
break
|
break
|
||||||
case DownsampleStrategy.CENTER_INSIDE_MEMORY || DownsampleStrategy.CENTER_INSIDE_QUALITY:
|
case DownsampleStrategy.CENTER_INSIDE_MEMORY || DownsampleStrategy.CENTER_INSIDE_QUALITY:
|
||||||
|
console.log('CENTER_INSIDE',JSON.stringify(downsampType))
|
||||||
exactScaleFactor = new CenterInside()
|
exactScaleFactor = new CenterInside()
|
||||||
.getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight,DownsampleStrategy.CENTER_INSIDE_MEMORY)
|
.getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight,downsampType)
|
||||||
rounding = new CenterInside()
|
rounding = new CenterInside()
|
||||||
.getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight,DownsampleStrategy.CENTER_INSIDE_MEMORY)
|
.getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight,downsampType)
|
||||||
break
|
break
|
||||||
|
|
||||||
case DownsampleStrategy.CENTER_OUTSIDE:
|
case DownsampleStrategy.CENTER_OUTSIDE:
|
||||||
|
@ -102,7 +103,7 @@ export class Downsampler {
|
||||||
.getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight)
|
.getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (exactScaleFactor <= 0 ||exactScaleFactor==undefined) {
|
if (exactScaleFactor==undefined|| exactScaleFactor <= 0 ) {
|
||||||
throw new Error("Cannot round with exactScaleFactor");
|
throw new Error("Cannot round with exactScaleFactor");
|
||||||
}
|
}
|
||||||
if (rounding == undefined) {
|
if (rounding == undefined) {
|
||||||
|
@ -110,10 +111,13 @@ export class Downsampler {
|
||||||
}
|
}
|
||||||
let outWidth: number = this.round(exactScaleFactor * orientedSourceWidth);
|
let outWidth: number = this.round(exactScaleFactor * orientedSourceWidth);
|
||||||
let outHeight: number = this.round(exactScaleFactor * orientedSourceHeight);
|
let outHeight: number = this.round(exactScaleFactor * orientedSourceHeight);
|
||||||
|
console.log('scaleFactor=outWidth==outHeight======',outWidth,outHeight)
|
||||||
let widthScaleFactor = orientedSourceWidth / outWidth;
|
let widthScaleFactor = orientedSourceWidth / outWidth;
|
||||||
let heightScaleFactor = orientedSourceHeight / outHeight;
|
let heightScaleFactor = orientedSourceHeight / outHeight;
|
||||||
let scaleFactor = rounding == 1 ? Math.max(widthScaleFactor, heightScaleFactor) :
|
console.log('scaleFactor=widthScaleFactor==heightScaleFactor======',widthScaleFactor,heightScaleFactor)
|
||||||
|
let scaleFactor = rounding ==SampleSizeRounding.QUALITY ? Math.max(widthScaleFactor, heightScaleFactor) :
|
||||||
Math.min(widthScaleFactor, heightScaleFactor) //将整型的缩放因子转换为2的次幂采样大小
|
Math.min(widthScaleFactor, heightScaleFactor) //将整型的缩放因子转换为2的次幂采样大小
|
||||||
|
console.log('scaleFactor===scaleFactor======',scaleFactor)
|
||||||
let powerOfTwoSampleSize: number = scaleFactor;
|
let powerOfTwoSampleSize: number = scaleFactor;
|
||||||
powerOfTwoSampleSize = Math.max(1, highestOneBit(scaleFactor))
|
powerOfTwoSampleSize = Math.max(1, highestOneBit(scaleFactor))
|
||||||
if (rounding == 1 && (powerOfTwoSampleSize < (1 / exactScaleFactor))) {
|
if (rounding == 1 && (powerOfTwoSampleSize < (1 / exactScaleFactor))) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { ImageKnifeOption } from '../ImageKnifeOption';
|
||||||
import { IEngineKey } from './IEngineKey';
|
import { IEngineKey } from './IEngineKey';
|
||||||
import { PixelMapTransformation } from '../transform/PixelMapTransformation';
|
import { PixelMapTransformation } from '../transform/PixelMapTransformation';
|
||||||
import { ImageKnifeRequestSource } from '../model/ImageKnifeData';
|
import { ImageKnifeRequestSource } from '../model/ImageKnifeData';
|
||||||
|
import { DownsampleStrategy } from '../downsampling/DownsampleStartegy';
|
||||||
|
|
||||||
@Sendable
|
@Sendable
|
||||||
export class DefaultEngineKey implements IEngineKey {
|
export class DefaultEngineKey implements IEngineKey {
|
||||||
|
|
Loading…
Reference in New Issue