From 68fbe7ef5119f191608b1ccb29c55e0decea15fd Mon Sep 17 00:00:00 2001 From: tsm <2418639820@qq.com> Date: Wed, 22 May 2024 15:25:31 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E9=99=8D=E9=87=87=E6=A0=B7=E9=80=82?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: tsm <2418639820@qq.com> --- entry/src/main/ets/pages/downsamplingPage.ets | 203 +++++------- .../ets/test/DefaultJobQueueTest.test.ets | 2 + library/index.ets | 10 +- .../downsampling/BaseDownsampling.ets | 15 - .../downsampling/DownsampleStartegy.ets | 311 +++++++++++------- .../imageknife/downsampling/Downsampler.ets | 121 ++++--- .../downsampling/downsampleUtils.ets | 19 ++ .../imageknife/utils/ParseImageUtil.ets | 15 +- .../imageknife/utils/gif/GIFParseImpl.ets | 5 +- .../imageknife/utils/svg/SVGParseImpl.ets | 5 +- 10 files changed, 376 insertions(+), 330 deletions(-) create mode 100644 library/src/main/ets/components/imageknife/downsampling/downsampleUtils.ets diff --git a/entry/src/main/ets/pages/downsamplingPage.ets b/entry/src/main/ets/pages/downsamplingPage.ets index 7465a87..f6b4c7a 100644 --- a/entry/src/main/ets/pages/downsamplingPage.ets +++ b/entry/src/main/ets/pages/downsamplingPage.ets @@ -1,116 +1,89 @@ -/* - * Copyright (C) 2024 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 { - FitCenter, - ImageKnifeComponent, +import { FitCenter, ImageKnifeComponent, ImageKnifeData, ImageKnifeGlobal, ImageKnifeOption, - RequestOption -} from '@ohos/libraryimageknife' + RequestOption } from '@ohos/libraryimageknife' import { BusinessError } from '@ohos.base' import { fitter, sampleNone } from '@ohos/imageknife'; - let pngUrl = $r('app.media.pngSample'); let jpgUrl = $r('app.media.jpgSample'); let svgUrl = $r('app.media.svgSample'); let gifUrl = $r('app.media.gifSample'); let bmpUrl = $r('app.media.bmpSample'); let webpUrl = $r('app.media.webpSample'); -let imageKnife = ImageKnifeGlobal.getInstance().getImageKnife(); - +let ImageKnife = ImageKnifeGlobal.getInstance().getImageKnife(); @Entry @Component struct Index { - @State originalMSquarePixelMap?: PixelMap = undefined; - @State mSquarePixelMap?: PixelMap = undefined; - @State h: number = 0 - @State w: number = 0 - @State originalBytesNumber: number = 0 - @State bytesNumber: number = 0 - @State url: string = '' - @State network: Boolean = false - @State imageKnifeOption: ImageKnifeOption = { + @State mSquarePixelMap1?: PixelMap = undefined; + @State mSquarePixelMap2?: PixelMap = undefined; + @State h:number = 0 + @State w:number = 0 + @State BytesNumber1:number = 0 + @State BytesNumber2:number = 0 + @State url:string = '' + @State network:Boolean = false + @State ImageKnifeOption1: ImageKnifeOption = { loadSrc: $r('app.media.icon'), placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed') } - transformSquare1(mUrl: string|Resource|PixelMap) { - let imageKnifeOption: RequestOption = new RequestOption(); + transformSquare1(mUrl:ESObject) { + let imageKnifeOption:RequestOption = new RequestOption(); imageKnifeOption.load(mUrl) - .addListener({ - callback: (err: BusinessError | string, data: ImageKnifeData) => { - this.originalMSquarePixelMap = data.drawPixelMap?.imagePixelMap as PixelMap; - this.originalBytesNumber = (data.drawPixelMap?.imagePixelMap as PixelMap).getPixelBytesNumber() - return false; - } - }) - imageKnife?.call(imageKnifeOption); + .addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => { + this.mSquarePixelMap1 = data.drawPixelMap?.imagePixelMap as PixelMap; + this.BytesNumber1 = (data.drawPixelMap?.imagePixelMap as PixelMap).getPixelBytesNumber() + return false; + } }) + ImageKnife?.call(imageKnifeOption); } - - transformSquare2(mUrl: string|Resource|PixelMap) { - let imageKnifeOption: RequestOption = new RequestOption(); + transformSquare2(mUrl:ESObject) { + let imageKnifeOption:RequestOption = new RequestOption(); let a = new fitter() 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; - } - }) - .setImageViewSize({ width: this.w, height: this.h }) + .addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => { + this.mSquarePixelMap2 = data.drawPixelMap?.imagePixelMap as PixelMap; + this.BytesNumber2 = (data.drawPixelMap?.imagePixelMap as PixelMap).getPixelBytesNumber() + return false; + } }) + .setImageViewSize({ width: this.w, height:this.h}) .downsampleOf(a) - imageKnife?.call(imageKnifeOption); + ImageKnife?.call(imageKnifeOption); } - @Builder TextInputSample() { Flex() { TextInput({ placeholder: '输入宽' }) - .onChange((enterKeyType) => { - this.w = Number(enterKeyType) + .onChange((EnterKeyType)=>{ + this.w =Number(EnterKeyType) }) TextInput({ placeholder: '输入高' }) - .onChange((enterKeyType) => { - this.h = Number(enterKeyType) + .onChange((EnterKeyType)=>{ + this.h =Number(EnterKeyType) }) }.padding(20) } build() { - Scroll() { + Scroll(){ Column() { - Row() { + Row(){ Button('切换网络图片') - .onClick(() => { - this.network = !this.network + .onClick(()=>{ + this.network = !this.network }) Text('输入网络图片url') TextInput({ placeholder: '输入url' }) - .onChange((urls) => { - this.url = urls + .onChange((urls)=>{ + this.url =urls }) } - - if (this.network) { - Column() { + if(this.network){ + Column(){ Text('原图') - Flex() { + Flex(){ Button('png') .onClick(() => { this.transformSquare1(this.url); @@ -141,12 +114,11 @@ struct Index { this.transformSquare1(this.url); }); - }.margin({ top: 20, bottom: 20 }) - - Text("原图字节大小:" + this.originalBytesNumber) - Column() { - if (this.originalMSquarePixelMap) { - Image(this.originalMSquarePixelMap == undefined ? '' : this.originalMSquarePixelMap!) + }.margin({top:20,bottom:20}) + Text("原图字节大小:"+this.BytesNumber1) + Column(){ + if (this.mSquarePixelMap1) { + Image(this.mSquarePixelMap1 == undefined ? '' : this.mSquarePixelMap1!) .objectFit(ImageFit.Fill) .width(200) .height(200) @@ -155,90 +127,82 @@ struct Index { Text('component用法') }.height(300).width('100%').backgroundColor(Color.Pink) - Text('降采样图片') - Flex() { + Flex(){ Button('png') .onClick(() => { this.transformSquare2(this.url); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: pngUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - downsampling: new fitter() } }); Button('svg') .onClick(() => { this.transformSquare2(this.url); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: svgUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - downsampling: new fitter() } }); Button('bmp') .onClick(() => { this.transformSquare2(this.url); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: bmpUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - downsampling: new fitter() } }); Button('jpg') .onClick(() => { this.transformSquare2(this.url); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: jpgUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - downsampling: new fitter() } }); Button('gif') .onClick(() => { // this.transformSquare2(this.url); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: gifUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - downsampling: new fitter() } }); Button('webp') .onClick(() => { // this.transformSquare2(this.url); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: webpUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - downsampling: new fitter() } }); - }.margin({ top: 20, bottom: 20 }) - - Text("降采样字节大小:" + this.bytesNumber) + }.margin({top:20,bottom:20}) + Text("降采样字节大小:"+this.BytesNumber2) this.TextInputSample() - Column() { - if (this.mSquarePixelMap) { - Image(this.mSquarePixelMap == undefined ? '' : this.mSquarePixelMap!) + Column(){ + if (this.mSquarePixelMap2) { + Image(this.mSquarePixelMap2 == undefined ? '' : this.mSquarePixelMap2!) .objectFit(ImageFit.Fill) .width(200) .height(200) .margin({ top: 10 }) } Text('component用法') - ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption }).width(200).height(200) + ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption1 }).width(200).height(200) }.height(300).width('100%').backgroundColor(Color.Pink) }.backgroundColor(Color.Orange) - } else { - Column() { + }else { + Column(){ Text('原图') - Flex() { + Flex(){ Button('png') .onClick(() => { this.transformSquare1(pngUrl); @@ -269,12 +233,11 @@ struct Index { this.transformSquare1(webpUrl); }); - }.margin({ top: 20, bottom: 20 }) - - Text("原图字节大小:" + this.originalBytesNumber) - Column() { - if (this.originalMSquarePixelMap) { - Image(this.originalMSquarePixelMap == undefined ? '' : this.originalMSquarePixelMap!) + }.margin({top:20,bottom:20}) + Text("原图字节大小:"+this.BytesNumber1) + Column(){ + if (this.mSquarePixelMap1) { + Image(this.mSquarePixelMap1 == undefined ? '' : this.mSquarePixelMap1!) .objectFit(ImageFit.Fill) .width(200) .height(200) @@ -283,13 +246,12 @@ struct Index { Text('component用法') }.height(300).width('100%').backgroundColor(Color.Pink) - Text('降采样图片') - Flex() { + Flex(){ Button('png') .onClick(() => { this.transformSquare2(pngUrl); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: pngUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), @@ -298,7 +260,7 @@ struct Index { Button('svg') .onClick(() => { this.transformSquare2(svgUrl); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: svgUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), @@ -308,7 +270,7 @@ struct Index { Button('bmp') .onClick(() => { this.transformSquare2(bmpUrl); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: bmpUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), @@ -317,7 +279,7 @@ struct Index { Button('jpg') .onClick(() => { this.transformSquare2(jpgUrl); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: jpgUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), @@ -326,7 +288,7 @@ struct Index { Button('gif') .onClick(() => { // this.transformSquare2(gifUrl); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: gifUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), @@ -335,29 +297,30 @@ struct Index { Button('webp') .onClick(() => { // this.transformSquare2(webpUrl); - this.imageKnifeOption = { + this.ImageKnifeOption1 = { loadSrc: webpUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), } }); - }.margin({ top: 20, bottom: 20 }) - - Text("降采样字节大小:" + this.bytesNumber) + }.margin({top:20,bottom:20}) + Text("降采样字节大小:"+this.BytesNumber2) this.TextInputSample() - Column() { - if (this.mSquarePixelMap) { - Image(this.mSquarePixelMap == undefined ? '' : this.mSquarePixelMap!) + Column(){ + if (this.mSquarePixelMap2) { + Image(this.mSquarePixelMap2 == undefined ? '' : this.mSquarePixelMap2!) .objectFit(ImageFit.Fill) .width(200) .height(200) .margin({ top: 10 }) } Text('component用法') - ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption }).width(200).height(200) + ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption1 }).width(200).height(200) }.height(300).width('100%').backgroundColor(Color.Pink) } } + + } } .height('100%') diff --git a/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets index 3212de0..fc221f3 100644 --- a/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets +++ b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets @@ -69,3 +69,5 @@ function makeRequest(src: string, context: common.UIAbilityContext, priority: ta request.moduleContext = context; return request; } + + diff --git a/library/index.ets b/library/index.ets index a4ed680..1d8e1a5 100644 --- a/library/index.ets +++ b/library/index.ets @@ -135,4 +135,12 @@ export { LogUtil } from './src/main/ets/components/imageknife/utils/LogUtil' /*下采样*/ export {Downsampler} from './src/main/ets/components/imageknife/downsampling/Downsampler' -export {DownsampleNone as sampleNone, FitCenter as fitter} from './src/main/ets/components/imageknife/downsampling/DownsampleStartegy' \ No newline at end of file +export { + DownsampleNone as sampleNone, + FitCenter as fitter, + CenterOutside, + Atleast, + AtMost, + CenterInside as Center_Inside + +} from './src/main/ets/components/imageknife/downsampling/DownsampleStartegy' \ 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 62a04a8..87ecafc 100644 --- a/library/src/main/ets/components/imageknife/downsampling/BaseDownsampling.ets +++ b/library/src/main/ets/components/imageknife/downsampling/BaseDownsampling.ets @@ -1,18 +1,3 @@ -/* - * Copyright (C) 2024 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 { lang } from '@kit.ArkTS'; type ISendable = lang.ISendable; diff --git a/library/src/main/ets/components/imageknife/downsampling/DownsampleStartegy.ets b/library/src/main/ets/components/imageknife/downsampling/DownsampleStartegy.ets index 9a756ed..c0ae2a9 100644 --- a/library/src/main/ets/components/imageknife/downsampling/DownsampleStartegy.ets +++ b/library/src/main/ets/components/imageknife/downsampling/DownsampleStartegy.ets @@ -12,138 +12,205 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -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, this.getScale(sourceWidth, sourceHeight, requestWidth, requestHeight)) - } - - getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - //下采样因子 - 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) - } -} - -/*不进行下采样*/ -@Sendable -export class DownsampleNone implements BaseDownsampling { - getName() { - return "DownsampleNone" - } - - getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - //下采样因子 - return 1 - } - - getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - //下采样因子 - return 1 - } -} - -/* 下采样使得图像的组大尺寸在给定的尺寸的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, this.highestOneBit(maxIntegerFactor)) - let greaterOrEqualSampleSize = lesserOrEqualSampleSize << (lesserOrEqualSampleSize < maxIntegerFactor ? 1 : 0) - return 1 / greaterOrEqualSampleSize - } - - getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - return 0 - } - - 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 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 / this.highestOneBit(minIntegerFactor) - } - - getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - //下采样因子 - 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 { - getName() { - return "CenterOutside" - } - - getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - let widthPercentage = requestWidth / sourceWidth - let heightPercentage = requestHeight / sourceHeight - return Math.max(widthPercentage, heightPercentage) - } - - getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - //下采样因子 - return 1 - } -} +import { BaseDownsampling } from './BaseDownsampling'; +import { highestOneBit, SampleSizeRounding } from './downsampleUtils'; +//FitCenter类实现DownsampleStartegy @Sendable export class FitCenter implements BaseDownsampling { getName() { return "FitCenter" } - getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - let widthPercentage = requestWidth / sourceWidth - let heightPercentage = requestHeight / sourceHeight - return Math.min(widthPercentage, heightPercentage) + //实现 getScaleFactor 方法 + getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { + const IS_BITMAP_FACTORY_SCALING_SUPPORTED = false; //这里需要根据实际情况设置这个值 + if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) { + const widthPercentage = requestedWidth / sourceWidth + const heightPercentage = requestedHeight / sourceHeight + return Math.min(widthPercentage, heightPercentage) + } else { + //类似 AT_LEAST,但只要求一个维度或另一个维度大于等于请求的尺寸 + const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth); + const a = maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor) + return a; + } } - getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number): number { - //下采样因子 - return 1 + //实现 getSampleSizeRounding 方法 + getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { + const IS_BITMAP_FACTORY_SCALING_SUPPORTED = true; //这里需要根据实际情况设置这个值 + + // if (!IS_BITMAP_FACTORY_SCALING_SUPPORTED) { + // return 0; + // } else { + // TODO: 这个逻辑可能不正确,但是如果不这样做,我们可能会跳过一个采样尺寸,因为 QUALITY 更喜欢宽度和高度缩放因子中比较小的那个 + // MEMORY 折中方案,他让我们更喜欢两者中较大的那个 + // + // } + return SampleSizeRounding.MEMORY; } } -export enum SampleSizeRounding { - MEMORY, - QUALITY +//None 类实现 DownsampleStrategy 接口 +@Sendable +export class DownsampleNone implements BaseDownsampling { + getName(): string { + return "DownsampleNone" + } + + //实现 getScaleFactor 方法 + public getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { + //不进行任何下采样,缩放因子为 1 + return 1; + } + + //实现 getSampleSizeRounding 方法 + public getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): SampleSizeRounding { + //总是返回 QUALITY + return SampleSizeRounding.QUALITY + + } +} + + +/*宽高进行等比缩放宽高里面最小的比例先放进去然后再更据原图的缩放比去适配另一边*/ +@Sendable +export class CenterOutside implements BaseDownsampling { + getName() { + return "CenterOutside" + } + + //实现 getScaleFactor 方法 + getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { + const widthPercentage = requestedWidth / sourceWidth; + const heightPercentage = requestedHeight / sourceHeight; + //返回宽度和高度比例中最大的值 + return Math.max(widthPercentage, heightPercentage); + } + + //实现 getSampleSizeRounding 方法 + getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): SampleSizeRounding { + //根据 CenterOutside 的逻辑,总是返回 QUALITY + return SampleSizeRounding.QUALITY; + } +} + +/*请求尺寸大于实际尺寸不进行放大,按照原图展示*/ +@Sendable +export class Atleast implements BaseDownsampling { + + getName() { + return "AtLeast" + } + + //实现 getScaleFactor 方法 + public getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): SampleSizeRounding { + //计算最小整数因子 + const minIntegerFactor = Math.min(sourceHeight / requestedHeight, sourceWidth / requestedWidth) + //根据最小整数因子计算缩放因子 + return minIntegerFactor === 0 ? 1 : 1 / highestOneBit(minIntegerFactor); + } + + //实现 getSampleSizeRounding 方法 + public getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): SampleSizeRounding { + //总是返回 QUALITY + return SampleSizeRounding.QUALITY + } +} + +/*请求尺寸大于实际尺寸不进行放大,按照原图展示*/ +@Sendable +export class AtMost implements BaseDownsampling { + getName() { + return "AtMost" + } + + //实现 getScaleFactor 方法 + getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { + const maxIntegerFactor = Math.ceil(Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth)); + let lesserOrEqualSampleSize = Math.max(1, highestOneBit(maxIntegerFactor)); + let greaterOrEqualSampleSize = lesserOrEqualSampleSize + // if (lesserOrEqualSampleSize < maxIntegerFactor) { + // let greaterOrEqualSampleSize = lesserOrEqualSampleSize <<= 1; // lest shift by 1 + // } + greaterOrEqualSampleSize = lesserOrEqualSampleSize << (lesserOrEqualSampleSize < maxIntegerFactor ? 1 : 0) + //返回缩放因子 + return 1 / greaterOrEqualSampleSize + } + + + + //实现 getSampleSizeRounding 方法 + getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestHeight: number): SampleSizeRounding { + //根据 AtMost 的逻辑,总是返回 MEMORY + return SampleSizeRounding.MEMORY + } +} + +/*宽高进行等比缩放宽高里面最大的比例先放进去然后再更据原图的缩放比去适配另一边*/ +@Sendable +export class CenterInside implements BaseDownsampling { + getName() { + return "CenterInside" + } + + //实现 getScaleFactor 方法 + getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { + //获取 FIT_CENTER 的缩放因子 + const fitCenterScaleFactor: ESObject = this.getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight); + //实现 getScaleFactor 方法 + //返回不超过 1 的缩放因子,即尽量缩小图像以适应目标尺寸,但不会放大 + return Math.min(1, fitCenterScaleFactor); + + } + + getScale(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { + const IS_BITMAP_FACTORY_SCALING_SUPPORTED = false; //这里需要根据实际情况设置这个值 + if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) { + const widthPercentage = requestedWidth / sourceWidth + const heightPercentage = requestedHeight / sourceHeight + return Math.min(widthPercentage, heightPercentage) + } else { + //类似 AT_LEAST,但只要求一个维度或另一个维度大于等于请求的尺寸 + const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth); + + return maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor); + + } + } + + + //实现 getSampleSizeRounding 方法 + getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): SampleSizeRounding { + //如果缩放因子为 1,表示没有缩放,优先选择质量 + if (this.getScaleFactor(sourceWidth, sourceHeight, requestedWidth, requestedHeight) === 1) { + return SampleSizeRounding.QUALITY + } + //否则,使用 FIL_CENTER 的 SampleSizeRounding 值 + return this.getSampleSize(sourceWidth, sourceHeight, requestedWidth, requestedHeight); + } + //实现 getSampleSizeRounding 方法 + getSampleSize(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): SampleSizeRounding { + const IS_BITMAP_FACTORY_SCALING_SUPPORTED = true; //这里需要根据实际情况设置这个值 + + if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) { + return 0; + } else { + // TODO: 这个逻辑可能不正确,但是如果不这样做,我们可能会跳过一个采样尺寸,因为 QUALITY 更喜欢宽度和高度缩放因子中比较小的那个 + // MEMORY 折中方案,他让我们更喜欢两者中较大的那个 + // + // } + return SampleSizeRounding.MEMORY; + } + } +} + +export enum DownsampleStrategy { + AT_LEAST, + AT_MOST, + FIT_CENTER, + CENTER_INSIDE, + CENTER_OUTSIDE, + NONE, } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/downsampling/Downsampler.ets b/library/src/main/ets/components/imageknife/downsampling/Downsampler.ets index 41aeabb..093baf1 100644 --- a/library/src/main/ets/components/imageknife/downsampling/Downsampler.ets +++ b/library/src/main/ets/components/imageknife/downsampling/Downsampler.ets @@ -1,26 +1,7 @@ -/* - * Copyright (C) 2024 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 } from '../RequestOption'; import { FileTypeUtil } from '../utils/FileTypeUtil'; -import { CenterOutside, FitCenter, SampleSizeRounding } from './DownsampleStartegy'; - -let TAG = 'downsampling' -export interface calculateScaleType { - targetWidth: number, - targetHeight: number -} +import { Atleast, AtMost, CenterInside, CenterOutside, DownsampleNone, FitCenter } from './DownsampleStartegy'; +import { calculateScaleType, highestOneBit, SampleSizeRounding } from './downsampleUtils'; export class Downsampler { @@ -29,40 +10,79 @@ export class Downsampler { sourceHeight: number, sourceWidth: number, request?: RequestOption - ): calculateScaleType { - const fileType: string | null = new FileTypeUtil().getFileType(imageType) - let powerOfTwoWidth: number | null = null; - let powerOfTwoHeight: number | null = null; + ): ESObject { + let degreesToRotate:number = 0 //获取旋转角度 + const fileType: string | null = new FileTypeUtil().getFileType(imageType)//获取图片类型 + let powerOfTwoWidth: number = 0; + let powerOfTwoHeight: number = 0; let targetWidth: number = 0 let targetHeight: number = 0 + if (sourceWidth <= 0 || sourceHeight <= 0) { + throw new Error("Cannot found width or height "); + }; + let orientedSourceWidth = sourceWidth; + let orientedSourceHeight = sourceHeight; + if (this.isRotationRequired(degreesToRotate)) { + orientedSourceWidth = sourceHeight; + orientedSourceHeight = sourceWidth; + } if (request?.size.width && !request?.size.height) { targetWidth = this.round((request?.size.height) * sourceWidth / sourceHeight) } else if (request?.size.height && !request?.size.width) { targetHeight = this.round((request?.size.width) * sourceHeight / sourceWidth) } else if (request?.size.height && request?.size.width) { - targetHeight = request.size.height; - targetWidth = request?.size.width; + targetWidth = request?.size.width===sourceWidth?(this.isRotationRequired(degreesToRotate)?sourceHeight:sourceWidth):request?.size.width + targetHeight = request?.size.height===sourceHeight?(this.isRotationRequired(degreesToRotate)?sourceWidth:sourceHeight):request?.size.height; } else { throw new Error("Cannot found width or height "); } - if (sourceWidth <= 0 || sourceHeight <= 0){ - throw new Error("Cannot found width or height"); - }; - let orientedSourceWidth = sourceWidth; - let orientedSourceHeight = sourceHeight; - if (this.isRotationRequired(90)) { - orientedSourceWidth = sourceHeight; - orientedSourceHeight = sourceWidth; - } /*安卓的模式*/ let exactScaleFactor: number = new FitCenter() .getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) - if (exactScaleFactor <= 0) { - throw new Error("Cannot round with exactScaleFactor"); - } /*安卓的模式*/ let rounding: SampleSizeRounding = new FitCenter() .getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + switch (request?.downsampType.getName()) { + case "FitCenter": + exactScaleFactor = new FitCenter() + .getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + rounding = new FitCenter() + .getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + break; + case "DownsampleNone": + exactScaleFactor = new DownsampleNone() + .getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + rounding = new DownsampleNone() + .getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + break + case "AtMost": + exactScaleFactor = new AtMost() + .getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + rounding = new AtMost() + .getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + break + case "CenterInside": + exactScaleFactor = new CenterInside() + .getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + rounding = new CenterInside() + .getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + break + case "AtLeast": + exactScaleFactor = new Atleast() + .getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + rounding = new Atleast() + .getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + break + case "CenterOutside": + exactScaleFactor = new CenterOutside() + .getScaleFactor(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + rounding = new CenterOutside() + .getSampleSizeRounding(orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight) + break + } + if (exactScaleFactor <= 0) { + throw new Error("Cannot round with exactScaleFactor"); + } if (rounding == null) { throw new Error("Cannot round with null rounding"); } @@ -75,7 +95,7 @@ export class Downsampler { Math.min(widthScaleFactor, heightScaleFactor) // 将整型的缩放因子转换为2的次幂采样大小 let powerOfTwoSampleSize: number = 0; - powerOfTwoSampleSize = Math.max(1, this.highestOneBit(scaleFactor)) + powerOfTwoSampleSize = Math.max(1, highestOneBit(scaleFactor)) if (rounding == SampleSizeRounding.MEMORY && powerOfTwoSampleSize < (1 / exactScaleFactor)) { powerOfTwoSampleSize = powerOfTwoSampleSize << 1; } @@ -103,15 +123,6 @@ export class Downsampler { 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); } @@ -119,16 +130,4 @@ export class Downsampler { isRotationRequired(degreesToRotate: number): boolean { return degreesToRotate == 90 || degreesToRotate == 270; } - - 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/downsampling/downsampleUtils.ets b/library/src/main/ets/components/imageknife/downsampling/downsampleUtils.ets new file mode 100644 index 0000000..bff773a --- /dev/null +++ b/library/src/main/ets/components/imageknife/downsampling/downsampleUtils.ets @@ -0,0 +1,19 @@ +//假设的枚举类型,与Java中的枚举相似 +export enum SampleSizeRounding { + QUALITY, + MEMORY +} + +export function highestOneBit(i: number): number { + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + return i - (i >>> 1); +} + +export interface calculateScaleType { + targetWidth: number, + targetHeight: number +} \ 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 eac523b..e7a403a 100644 --- a/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets +++ b/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets @@ -17,19 +17,20 @@ import { IParseImage } from '../interface/IParseImage' import image from '@ohos.multimedia.image'; import { BusinessError } from '@ohos.base' import { RequestOption } from '../RequestOption'; -import {Downsampler,calculateScaleType } from '../downsampling/Downsampler'; +import { Downsampler } from '../downsampling/Downsampler'; +import { calculateScaleType } from '../Downsampling/downsampleUtils'; export class ParseImageUtil implements IParseImage { parseImage( imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void, - request?:RequestOption + request?: RequestOption ) { - this.parseImageThumbnail(1, imageinfo, onCompleteFunction, onErrorFunction,request) + this.parseImageThumbnail(1, imageinfo, onCompleteFunction, onErrorFunction, request) } - parseImageThumbnail(scale: number, imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void,request?:RequestOption) { + parseImageThumbnail(scale: number, imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void, request?: RequestOption) { let imageSource: image.ImageSource = image.createImageSource(imageinfo); // 步骤一:文件转为pixelMap 然后变换 给Image组件 imageSource.getImageInfo().then((value) => { @@ -43,9 +44,9 @@ export class ParseImageUtil implements IParseImage { editable: true, desiredSize: defaultSize }; - if(request?.downsampType.getName()!==undefined && request?.downsampType.getName()!=='DownsampleNone'){ - const b:calculateScaleType = new Downsampler().calculateScaling(imageinfo, hValue, wValue,request) - opts= { + if (request?.downsampType.getName() !== undefined && request?.downsampType.getName() !== 'DownsampleNone') { + const b: calculateScaleType = new Downsampler().calculateScaling(imageinfo, hValue, wValue, request) + opts = { editable: true, desiredSize: { height: b.targetHeight, 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 9c4ea5f..981ea59 100644 --- a/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets +++ b/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets @@ -20,7 +20,8 @@ import worker, { ErrorEvent, MessageEvents } from '@ohos.worker'; import taskpool from '@ohos.taskpool' import { LogUtil } from '../LogUtil' import { RequestOption } from '../../RequestOption' -import { Downsampler,calculateScaleType } from '../../downsampling/Downsampler' +import { Downsampler } from '../../downsampling/Downsampler' +import { calculateScaleType } from '../../Downsampling/downsampleUtils' export interface senderData { type: string, @@ -53,7 +54,7 @@ export class GIFParseImpl implements IParseGif { imageSource.getImageInfo().then((value) => { hValue = Math.round(value.size.height); wValue = Math.round(value.size.height); - if ( _request?.downsampType.getName()!==undefined && _request?.downsampType.getName()!=='DownsampleNone') { + if (_request?.downsampType.getName() !== undefined && _request?.downsampType.getName() !== 'DownsampleNone') { const b: calculateScaleType = new Downsampler().calculateScaling(imageinfo, Math.round(value.size.height), Math.round(value.size.width), _request) decodeOpts = { sampleSize: 1, 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 e7df537..20d64d2 100644 --- a/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets +++ b/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets @@ -17,7 +17,8 @@ import { RequestOption } from '../../RequestOption' import { BusinessError } from '@ohos.base' import { ImageKnifeData, ImageKnifeType } from '../../ImageKnifeData' import image from '@ohos.multimedia.image' -import { Downsampler, calculateScaleType } from '../../downsampling/Downsampler' +import { Downsampler } from '../../downsampling/Downsampler' +import { calculateScaleType } from '../../Downsampling/downsampleUtils' export class SVGParseImpl implements IParseSvg { parseSvg(option: RequestOption, imageInfo: ArrayBuffer, @@ -36,7 +37,7 @@ export class SVGParseImpl implements IParseSvg { editable: true, desiredSize: defaultSize }; - if (option?.downsampType.getName()!==undefined && option?.downsampType.getName()!=='DownsampleNone') { + if (option?.downsampType.getName() !== undefined && option?.downsampType.getName() !== 'DownsampleNone') { const b: calculateScaleType = new Downsampler().calculateScaling(imageInfo, hValue, wValue, option) opts = { editable: true, From 620abe646800ce90f909d918248e26fd05ab75f1 Mon Sep 17 00:00:00 2001 From: tsm <2418639820@qq.com> Date: Wed, 22 May 2024 16:30:02 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E9=99=8D=E9=87=87=E6=A0=B7=E9=80=82?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: tsm <2418639820@qq.com> --- .../downsampling/DownsampleStartegy.ets | 67 ++----------------- 1 file changed, 7 insertions(+), 60 deletions(-) diff --git a/library/src/main/ets/components/imageknife/downsampling/DownsampleStartegy.ets b/library/src/main/ets/components/imageknife/downsampling/DownsampleStartegy.ets index c0ae2a9..8223d1e 100644 --- a/library/src/main/ets/components/imageknife/downsampling/DownsampleStartegy.ets +++ b/library/src/main/ets/components/imageknife/downsampling/DownsampleStartegy.ets @@ -24,30 +24,13 @@ export class FitCenter implements BaseDownsampling { //实现 getScaleFactor 方法 getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { - const IS_BITMAP_FACTORY_SCALING_SUPPORTED = false; //这里需要根据实际情况设置这个值 - if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) { - const widthPercentage = requestedWidth / sourceWidth - const heightPercentage = requestedHeight / sourceHeight - return Math.min(widthPercentage, heightPercentage) - } else { - //类似 AT_LEAST,但只要求一个维度或另一个维度大于等于请求的尺寸 - const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth); - const a = maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor) - return a; - } + const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth); + const a = maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor) + return a; } - //实现 getSampleSizeRounding 方法 getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { - const IS_BITMAP_FACTORY_SCALING_SUPPORTED = true; //这里需要根据实际情况设置这个值 - // if (!IS_BITMAP_FACTORY_SCALING_SUPPORTED) { - // return 0; - // } else { - // TODO: 这个逻辑可能不正确,但是如果不这样做,我们可能会跳过一个采样尺寸,因为 QUALITY 更喜欢宽度和高度缩放因子中比较小的那个 - // MEMORY 折中方案,他让我们更喜欢两者中较大的那个 - // - // } return SampleSizeRounding.MEMORY; } } @@ -131,16 +114,10 @@ export class AtMost implements BaseDownsampling { const maxIntegerFactor = Math.ceil(Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth)); let lesserOrEqualSampleSize = Math.max(1, highestOneBit(maxIntegerFactor)); let greaterOrEqualSampleSize = lesserOrEqualSampleSize - // if (lesserOrEqualSampleSize < maxIntegerFactor) { - // let greaterOrEqualSampleSize = lesserOrEqualSampleSize <<= 1; // lest shift by 1 - // } greaterOrEqualSampleSize = lesserOrEqualSampleSize << (lesserOrEqualSampleSize < maxIntegerFactor ? 1 : 0) //返回缩放因子 return 1 / greaterOrEqualSampleSize } - - - //实现 getSampleSizeRounding 方法 getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestHeight: number): SampleSizeRounding { //根据 AtMost 的逻辑,总是返回 MEMORY @@ -159,25 +136,15 @@ export class CenterInside implements BaseDownsampling { getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { //获取 FIT_CENTER 的缩放因子 const fitCenterScaleFactor: ESObject = this.getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight); - //实现 getScaleFactor 方法 //返回不超过 1 的缩放因子,即尽量缩小图像以适应目标尺寸,但不会放大 return Math.min(1, fitCenterScaleFactor); } getScale(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { - const IS_BITMAP_FACTORY_SCALING_SUPPORTED = false; //这里需要根据实际情况设置这个值 - if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) { - const widthPercentage = requestedWidth / sourceWidth - const heightPercentage = requestedHeight / sourceHeight - return Math.min(widthPercentage, heightPercentage) - } else { - //类似 AT_LEAST,但只要求一个维度或另一个维度大于等于请求的尺寸 - const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth); - - return maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor); - - } + //类似 AT_LEAST + const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth); + return maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor); } @@ -190,27 +157,7 @@ export class CenterInside implements BaseDownsampling { //否则,使用 FIL_CENTER 的 SampleSizeRounding 值 return this.getSampleSize(sourceWidth, sourceHeight, requestedWidth, requestedHeight); } - //实现 getSampleSizeRounding 方法 getSampleSize(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): SampleSizeRounding { - const IS_BITMAP_FACTORY_SCALING_SUPPORTED = true; //这里需要根据实际情况设置这个值 - - if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) { - return 0; - } else { - // TODO: 这个逻辑可能不正确,但是如果不这样做,我们可能会跳过一个采样尺寸,因为 QUALITY 更喜欢宽度和高度缩放因子中比较小的那个 - // MEMORY 折中方案,他让我们更喜欢两者中较大的那个 - // - // } - return SampleSizeRounding.MEMORY; - } + return SampleSizeRounding.MEMORY; } -} - -export enum DownsampleStrategy { - AT_LEAST, - AT_MOST, - FIT_CENTER, - CENTER_INSIDE, - CENTER_OUTSIDE, - NONE, } \ No newline at end of file From c4f43817a11fe7a85f1ddeac5a52a7e44a6fd7b0 Mon Sep 17 00:00:00 2001 From: tsm <2418639820@qq.com> Date: Wed, 22 May 2024 16:33:57 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E9=99=8D=E9=87=87=E6=A0=B7=E9=80=82?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: tsm <2418639820@qq.com> --- entry/src/main/ets/pages/downsamplingPage.ets | 203 +++++++++++------- 1 file changed, 120 insertions(+), 83 deletions(-) diff --git a/entry/src/main/ets/pages/downsamplingPage.ets b/entry/src/main/ets/pages/downsamplingPage.ets index f6b4c7a..7465a87 100644 --- a/entry/src/main/ets/pages/downsamplingPage.ets +++ b/entry/src/main/ets/pages/downsamplingPage.ets @@ -1,89 +1,116 @@ -import { FitCenter, ImageKnifeComponent, +/* + * Copyright (C) 2024 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 { + FitCenter, + ImageKnifeComponent, ImageKnifeData, ImageKnifeGlobal, ImageKnifeOption, - RequestOption } from '@ohos/libraryimageknife' + RequestOption +} from '@ohos/libraryimageknife' import { BusinessError } from '@ohos.base' import { fitter, sampleNone } from '@ohos/imageknife'; + let pngUrl = $r('app.media.pngSample'); let jpgUrl = $r('app.media.jpgSample'); let svgUrl = $r('app.media.svgSample'); let gifUrl = $r('app.media.gifSample'); let bmpUrl = $r('app.media.bmpSample'); let webpUrl = $r('app.media.webpSample'); -let ImageKnife = ImageKnifeGlobal.getInstance().getImageKnife(); +let imageKnife = ImageKnifeGlobal.getInstance().getImageKnife(); + @Entry @Component struct Index { - @State mSquarePixelMap1?: PixelMap = undefined; - @State mSquarePixelMap2?: PixelMap = undefined; - @State h:number = 0 - @State w:number = 0 - @State BytesNumber1:number = 0 - @State BytesNumber2:number = 0 - @State url:string = '' - @State network:Boolean = false - @State ImageKnifeOption1: ImageKnifeOption = { + @State originalMSquarePixelMap?: PixelMap = undefined; + @State mSquarePixelMap?: PixelMap = undefined; + @State h: number = 0 + @State w: number = 0 + @State originalBytesNumber: number = 0 + @State bytesNumber: number = 0 + @State url: string = '' + @State network: Boolean = false + @State imageKnifeOption: ImageKnifeOption = { loadSrc: $r('app.media.icon'), placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed') } - transformSquare1(mUrl:ESObject) { - let imageKnifeOption:RequestOption = new RequestOption(); + transformSquare1(mUrl: string|Resource|PixelMap) { + let imageKnifeOption: RequestOption = 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; - } }) - ImageKnife?.call(imageKnifeOption); + .addListener({ + callback: (err: BusinessError | string, data: ImageKnifeData) => { + this.originalMSquarePixelMap = data.drawPixelMap?.imagePixelMap as PixelMap; + this.originalBytesNumber = (data.drawPixelMap?.imagePixelMap as PixelMap).getPixelBytesNumber() + return false; + } + }) + imageKnife?.call(imageKnifeOption); } - transformSquare2(mUrl:ESObject) { - let imageKnifeOption:RequestOption = new RequestOption(); + + transformSquare2(mUrl: string|Resource|PixelMap) { + let imageKnifeOption: RequestOption = new RequestOption(); let a = new fitter() imageKnifeOption.load(mUrl) - .addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => { - this.mSquarePixelMap2 = data.drawPixelMap?.imagePixelMap as PixelMap; - this.BytesNumber2 = (data.drawPixelMap?.imagePixelMap as PixelMap).getPixelBytesNumber() - return false; - } }) - .setImageViewSize({ width: this.w, height:this.h}) + .addListener({ + callback: (err: BusinessError | string, data: ImageKnifeData) => { + this.mSquarePixelMap = data.drawPixelMap?.imagePixelMap as PixelMap; + this.bytesNumber = (data.drawPixelMap?.imagePixelMap as PixelMap).getPixelBytesNumber() + return false; + } + }) + .setImageViewSize({ width: this.w, height: this.h }) .downsampleOf(a) - ImageKnife?.call(imageKnifeOption); + imageKnife?.call(imageKnifeOption); } + @Builder TextInputSample() { Flex() { TextInput({ placeholder: '输入宽' }) - .onChange((EnterKeyType)=>{ - this.w =Number(EnterKeyType) + .onChange((enterKeyType) => { + this.w = Number(enterKeyType) }) TextInput({ placeholder: '输入高' }) - .onChange((EnterKeyType)=>{ - this.h =Number(EnterKeyType) + .onChange((enterKeyType) => { + this.h = Number(enterKeyType) }) }.padding(20) } build() { - Scroll(){ + Scroll() { Column() { - Row(){ + Row() { Button('切换网络图片') - .onClick(()=>{ - this.network = !this.network + .onClick(() => { + this.network = !this.network }) Text('输入网络图片url') TextInput({ placeholder: '输入url' }) - .onChange((urls)=>{ - this.url =urls + .onChange((urls) => { + this.url = urls }) } - if(this.network){ - Column(){ + + if (this.network) { + Column() { Text('原图') - Flex(){ + Flex() { Button('png') .onClick(() => { this.transformSquare1(this.url); @@ -114,11 +141,12 @@ struct Index { this.transformSquare1(this.url); }); - }.margin({top:20,bottom:20}) - Text("原图字节大小:"+this.BytesNumber1) - Column(){ - if (this.mSquarePixelMap1) { - Image(this.mSquarePixelMap1 == undefined ? '' : this.mSquarePixelMap1!) + }.margin({ top: 20, bottom: 20 }) + + Text("原图字节大小:" + this.originalBytesNumber) + Column() { + if (this.originalMSquarePixelMap) { + Image(this.originalMSquarePixelMap == undefined ? '' : this.originalMSquarePixelMap!) .objectFit(ImageFit.Fill) .width(200) .height(200) @@ -127,82 +155,90 @@ struct Index { Text('component用法') }.height(300).width('100%').backgroundColor(Color.Pink) + Text('降采样图片') - Flex(){ + Flex() { Button('png') .onClick(() => { this.transformSquare2(this.url); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: pngUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), + downsampling: new fitter() } }); Button('svg') .onClick(() => { this.transformSquare2(this.url); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: svgUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), + downsampling: new fitter() } }); Button('bmp') .onClick(() => { this.transformSquare2(this.url); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: bmpUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), + downsampling: new fitter() } }); Button('jpg') .onClick(() => { this.transformSquare2(this.url); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: jpgUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), + downsampling: new fitter() } }); Button('gif') .onClick(() => { // this.transformSquare2(this.url); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: gifUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), + downsampling: new fitter() } }); Button('webp') .onClick(() => { // this.transformSquare2(this.url); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: webpUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), + downsampling: new fitter() } }); - }.margin({top:20,bottom:20}) - Text("降采样字节大小:"+this.BytesNumber2) + }.margin({ top: 20, bottom: 20 }) + + Text("降采样字节大小:" + this.bytesNumber) this.TextInputSample() - Column(){ - if (this.mSquarePixelMap2) { - Image(this.mSquarePixelMap2 == undefined ? '' : this.mSquarePixelMap2!) + Column() { + if (this.mSquarePixelMap) { + Image(this.mSquarePixelMap == undefined ? '' : this.mSquarePixelMap!) .objectFit(ImageFit.Fill) .width(200) .height(200) .margin({ top: 10 }) } Text('component用法') - ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption1 }).width(200).height(200) + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption }).width(200).height(200) }.height(300).width('100%').backgroundColor(Color.Pink) }.backgroundColor(Color.Orange) - }else { - Column(){ + } else { + Column() { Text('原图') - Flex(){ + Flex() { Button('png') .onClick(() => { this.transformSquare1(pngUrl); @@ -233,11 +269,12 @@ struct Index { this.transformSquare1(webpUrl); }); - }.margin({top:20,bottom:20}) - Text("原图字节大小:"+this.BytesNumber1) - Column(){ - if (this.mSquarePixelMap1) { - Image(this.mSquarePixelMap1 == undefined ? '' : this.mSquarePixelMap1!) + }.margin({ top: 20, bottom: 20 }) + + Text("原图字节大小:" + this.originalBytesNumber) + Column() { + if (this.originalMSquarePixelMap) { + Image(this.originalMSquarePixelMap == undefined ? '' : this.originalMSquarePixelMap!) .objectFit(ImageFit.Fill) .width(200) .height(200) @@ -246,12 +283,13 @@ struct Index { Text('component用法') }.height(300).width('100%').backgroundColor(Color.Pink) + Text('降采样图片') - Flex(){ + Flex() { Button('png') .onClick(() => { this.transformSquare2(pngUrl); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: pngUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), @@ -260,7 +298,7 @@ struct Index { Button('svg') .onClick(() => { this.transformSquare2(svgUrl); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: svgUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), @@ -270,7 +308,7 @@ struct Index { Button('bmp') .onClick(() => { this.transformSquare2(bmpUrl); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: bmpUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), @@ -279,7 +317,7 @@ struct Index { Button('jpg') .onClick(() => { this.transformSquare2(jpgUrl); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: jpgUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), @@ -288,7 +326,7 @@ struct Index { Button('gif') .onClick(() => { // this.transformSquare2(gifUrl); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: gifUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), @@ -297,30 +335,29 @@ struct Index { Button('webp') .onClick(() => { // this.transformSquare2(webpUrl); - this.ImageKnifeOption1 = { + this.imageKnifeOption = { loadSrc: webpUrl, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), } }); - }.margin({top:20,bottom:20}) - Text("降采样字节大小:"+this.BytesNumber2) + }.margin({ top: 20, bottom: 20 }) + + Text("降采样字节大小:" + this.bytesNumber) this.TextInputSample() - Column(){ - if (this.mSquarePixelMap2) { - Image(this.mSquarePixelMap2 == undefined ? '' : this.mSquarePixelMap2!) + Column() { + if (this.mSquarePixelMap) { + Image(this.mSquarePixelMap == undefined ? '' : this.mSquarePixelMap!) .objectFit(ImageFit.Fill) .width(200) .height(200) .margin({ top: 10 }) } Text('component用法') - ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption1 }).width(200).height(200) + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption }).width(200).height(200) }.height(300).width('100%').backgroundColor(Color.Pink) } } - - } } .height('100%') From 914b9b87eb0f6f7d256a54294dc87960189c8df1 Mon Sep 17 00:00:00 2001 From: tsm <2418639820@qq.com> Date: Wed, 22 May 2024 16:36:59 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E9=99=8D=E9=87=87=E6=A0=B7=E9=80=82?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: tsm <2418639820@qq.com> --- entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets index fc221f3..cfdffac 100644 --- a/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets +++ b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets @@ -68,6 +68,4 @@ function makeRequest(src: string, context: common.UIAbilityContext, priority: ta request.priority = priority; request.moduleContext = context; return request; -} - - +} \ No newline at end of file