Pre Merge pull request !389 from 田双明/master
This commit is contained in:
commit
6c94566042
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
## 3.1.1-rc.0
|
## 3.1.1-rc.0
|
||||||
- 重构代码:抽取ImageKnifeDispatcher子线程requestJob相关代码到ImageKnifeLoader中,降低函数复杂度
|
- 重构代码:抽取ImageKnifeDispatcher子线程requestJob相关代码到ImageKnifeLoader中,降低函数复杂度
|
||||||
|
- 降采样功能
|
||||||
## 3.1.0
|
## 3.1.0
|
||||||
- 部分静态webp图片有delay属性导致识别成动图,改用getFrameCount识别
|
- 部分静态webp图片有delay属性导致识别成动图,改用getFrameCount识别
|
||||||
- 修复加载错误图后未去请求排队队列中的请求
|
- 修复加载错误图后未去请求排队队列中的请求
|
||||||
|
|
23
README.md
23
README.md
|
@ -264,6 +264,17 @@ ImageKnifeAnimatorComponent({
|
||||||
}),animatorOption:this.animatorOption
|
}),animatorOption:this.animatorOption
|
||||||
}).width(300).height(300).backgroundColor(Color.Orange).margin({top:30})
|
}).width(300).height(300).backgroundColor(Color.Orange).margin({top:30})
|
||||||
```
|
```
|
||||||
|
#### 11.图片降采样 示例
|
||||||
|
```
|
||||||
|
ImageKnifeComponent({
|
||||||
|
imageKnifeOption:new ImageKnifeOption({
|
||||||
|
loadSrc:$r("app.media.pngSample"),
|
||||||
|
placeholderSrc:$r('app.media.loading'),
|
||||||
|
errorholderSrc:$r('app.media.failed'),
|
||||||
|
downsampleOf: DownsampleStrategy.NONE
|
||||||
|
}),animatorOption:this.animatorOption
|
||||||
|
}).width(300).height(300)
|
||||||
|
```
|
||||||
#### Reuse Scenario
|
#### Reuse Scenario
|
||||||
Clear the component content in the **aboutToRecycle** lifecycle and trigger image loading through watch observeration.
|
Clear the component content in the **aboutToRecycle** lifecycle and trigger image loading through watch observeration.
|
||||||
## Available APIs
|
## Available APIs
|
||||||
|
@ -308,7 +319,17 @@ Clear the component content in the **aboutToRecycle** lifecycle and trigger imag
|
||||||
| drawingColorFilter | ColorFilter | Drawing color filter. Optional. |
|
| drawingColorFilter | ColorFilter | Drawing color filter. Optional. |
|
||||||
| onComplete | (event:EventImage \| undefined)=>void | Callback for image loading completion. Optional. |
|
| onComplete | (event:EventImage \| undefined)=>void | Callback for image loading completion. Optional. |
|
||||||
| onLoadListener | onLoadStart:()=>void,onLoadSuccess:(data:string\|Pixelmap)=>void | Callback for image loading events. Optional. |
|
| onLoadListener | onLoadStart:()=>void,onLoadSuccess:(data:string\|Pixelmap)=>void | Callback for image loading events. Optional. |
|
||||||
|
| downsampleOf | DownsampleStrategy | 降采样(可选) |
|
||||||
|
### 降采样类型
|
||||||
|
| 类型 | 相关描述 |
|
||||||
|
|---------------------|-------------------|
|
||||||
|
| NONE | 不进行降采样 |
|
||||||
|
| AT_MOST | 请求尺寸大于实际尺寸不进行放大 |
|
||||||
|
| FIT_CENTER_MEMORY | 两边自适应内存优先 |
|
||||||
|
| FIT_CENTER_QUALITY | 两边自适应质量优先 |
|
||||||
|
| CENTER_INSIDE_MEMORY | 宽高缩放比最大的比例,进行缩放适配内存优先 |
|
||||||
|
| CENTER_INSIDE_QUALITY | 宽高缩放比最大的比例,进行缩放适配质量优先 |
|
||||||
|
| CENTER_OUTSIDE | 根据宽高的最小的比例,进行适配 |
|
||||||
### ImageKnife
|
### ImageKnife
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
* 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 '@ohos/imageknife';
|
||||||
import { IEngineKey, ImageKnifeOption, PixelMapTransformation,SparkMD5 ,ImageKnifeRequestSource} from '@ohos/libraryimageknife';
|
import { IEngineKey, ImageKnifeOption, PixelMapTransformation,SparkMD5 ,ImageKnifeRequestSource} from '@ohos/libraryimageknife';
|
||||||
|
|
||||||
//全局自定义key demo
|
//全局自定义key demo
|
||||||
|
@ -35,6 +36,9 @@ export class CustomEngineKeyImpl implements IEngineKey {
|
||||||
if (imageKnifeOption.transformation) {
|
if (imageKnifeOption.transformation) {
|
||||||
key += "transformation=" + this.getTransformation(imageKnifeOption.transformation) + ";"
|
key += "transformation=" + this.getTransformation(imageKnifeOption.transformation) + ";"
|
||||||
}
|
}
|
||||||
|
if ((imageKnifeOption.downsampleOf !== DownsampleStrategy.NONE && imageKnifeOption.downsampleOf !== undefined)) {
|
||||||
|
key += "downsampleOf" + imageKnifeOption.downsampleOf +"width="+width+"height="+ height
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
/*
|
||||||
|
* 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 { DownsampleStrategy, ImageKnifeOption, } from '@ohos/imageknife';
|
||||||
|
import { ImageKnifeComponent } from '@ohos/libraryimageknife';
|
||||||
|
import { image } from '@kit.ImageKit';
|
||||||
|
import { BusinessError } from '@kit.BasicServicesKit';
|
||||||
|
import { Downsampler } from '@ohos/imageknife/src/main/ets/downsampling/Downsampler';
|
||||||
|
import { FileTypeUtil } from '@ohos/imageknife/src/main/ets/utils/FileTypeUtil';
|
||||||
|
|
||||||
|
@Entry
|
||||||
|
@ComponentV2
|
||||||
|
struct DownSamplePage {
|
||||||
|
@Local imageKnifeOption: ImageKnifeOption = new ImageKnifeOption({
|
||||||
|
loadSrc: $r('app.media.startIcon'),
|
||||||
|
placeholderSrc: $r("app.media.loading"),
|
||||||
|
errorholderSrc: $r("app.media.app_icon"),
|
||||||
|
objectFit: ImageFit.Contain
|
||||||
|
})
|
||||||
|
isBrightness: boolean = false
|
||||||
|
@Local beforeSampling: number = 0
|
||||||
|
@Local afterSampling: number = 0
|
||||||
|
@Local SamplingList: SamplingType[] = [
|
||||||
|
new SamplingType(0, "NONE"),
|
||||||
|
new SamplingType(1, "AT_MOST"),
|
||||||
|
new SamplingType(2, "FIT_CENTER_MEMORY"),
|
||||||
|
new SamplingType(4, "FIT_CENTER_QUALITY"),
|
||||||
|
new SamplingType(5, "CENTER_INSIDE_MEMORY"),
|
||||||
|
new SamplingType(6, "CENTER_INSIDE_QUALITY"),
|
||||||
|
new SamplingType(7, "CENTER_OUTSIDE"),
|
||||||
|
]
|
||||||
|
@Local checked: boolean = false
|
||||||
|
|
||||||
|
updateImageKnifeOption(value: string) {
|
||||||
|
if (value === 'NONE') {
|
||||||
|
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.NONE
|
||||||
|
})
|
||||||
|
this.originalPixMap($r('app.media.pngSample'))
|
||||||
|
this.afterSamplingFunc($r('app.media.pngSample'))
|
||||||
|
} else if (value === 'AT_MOST') {
|
||||||
|
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.AT_MOST
|
||||||
|
})
|
||||||
|
this.originalPixMap($r('app.media.pngSample'))
|
||||||
|
this.afterSamplingFunc($r('app.media.pngSample'))
|
||||||
|
} else if (value === 'FIT_CENTER_MEMORY') {
|
||||||
|
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_MEMORY
|
||||||
|
})
|
||||||
|
this.originalPixMap($r('app.media.pngSample'))
|
||||||
|
this.afterSamplingFunc($r('app.media.pngSample'))
|
||||||
|
} 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({
|
||||||
|
loadSrc: $r('app.media.pngSample'),
|
||||||
|
placeholderSrc: $r("app.media.loading"),
|
||||||
|
errorholderSrc: $r("app.media.app_icon"),
|
||||||
|
objectFit: ImageFit.Contain,
|
||||||
|
downsampleOf: DownsampleStrategy.CENTER_INSIDE_MEMORY
|
||||||
|
})
|
||||||
|
this.originalPixMap($r('app.media.pngSample'))
|
||||||
|
this.afterSamplingFunc($r('app.media.pngSample'))
|
||||||
|
} else if (value === 'CENTER_INSIDE_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.CENTER_INSIDE_QUALITY
|
||||||
|
})
|
||||||
|
this.originalPixMap($r('app.media.pngSample'))
|
||||||
|
this.afterSamplingFunc($r('app.media.pngSample'))
|
||||||
|
} else {
|
||||||
|
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.CENTER_OUTSIDE
|
||||||
|
})
|
||||||
|
this.originalPixMap($r('app.media.pngSample'))
|
||||||
|
this.afterSamplingFunc($r('app.media.pngSample'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async afterSamplingFunc(imgs: Resource) {
|
||||||
|
let img: Uint8Array = await getContext(this).resourceManager.getMediaContent(imgs);
|
||||||
|
let imageSource: image.ImageSource = image.createImageSource(img.buffer.slice(0));
|
||||||
|
let fileTypeUtil = new FileTypeUtil();
|
||||||
|
let typeValue = fileTypeUtil.getFileType(img.buffer.slice(0)) as string;
|
||||||
|
let decodingOptions: image.DecodingOptions = {
|
||||||
|
editable: true,
|
||||||
|
desiredPixelFormat: 3,
|
||||||
|
}
|
||||||
|
let imageInfo = await imageSource.getImageInfo()
|
||||||
|
let reqSize =
|
||||||
|
new Downsampler().calculateScaling(typeValue, imageInfo.size.width, imageInfo.size.height, 300,
|
||||||
|
300, this.imageKnifeOption.downsampleOf)
|
||||||
|
decodingOptions = {
|
||||||
|
editable: true,
|
||||||
|
desiredSize: {
|
||||||
|
width: reqSize.width,
|
||||||
|
height: reqSize.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 创建pixelMap
|
||||||
|
imageSource.createPixelMap(decodingOptions).then((pixelMap: image.PixelMap) => {
|
||||||
|
this.afterSampling = pixelMap.getPixelBytesNumber()
|
||||||
|
}).catch((err: BusinessError) => {
|
||||||
|
console.error("Failed to create PixelMap")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async originalPixMap(imgs: Resource,) {
|
||||||
|
let img: Uint8Array = await getContext(this).resourceManager.getMediaContent(imgs);
|
||||||
|
let imageSource: image.ImageSource = image.createImageSource(img.buffer.slice(0));
|
||||||
|
let decodingOptions: image.DecodingOptions = {
|
||||||
|
editable: true,
|
||||||
|
desiredPixelFormat: 3,
|
||||||
|
}
|
||||||
|
// 创建pixelMap
|
||||||
|
imageSource.createPixelMap(decodingOptions).then((pixelMap: image.PixelMap) => {
|
||||||
|
this.beforeSampling = pixelMap.getPixelBytesNumber()
|
||||||
|
}).catch((err: BusinessError) => {
|
||||||
|
console.error("Failed to create PixelMap")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Scroll() {
|
||||||
|
Column() {
|
||||||
|
ForEach(this.SamplingList, (item: SamplingType, index) => {
|
||||||
|
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
|
||||||
|
Radio({ value: item.value + 'radio', group: 'radioGroup' })
|
||||||
|
.height(50)
|
||||||
|
.width(50)
|
||||||
|
.checked(this.checked)
|
||||||
|
.onClick(() => {
|
||||||
|
this.updateImageKnifeOption(item.value)
|
||||||
|
})
|
||||||
|
Text("降采样规格:" + item.value).fontSize(20)
|
||||||
|
}
|
||||||
|
}, (item: SamplingType) => JSON.stringify(item))
|
||||||
|
Column() {
|
||||||
|
Text(`未降采样大小:${this.beforeSampling}`).fontSize(20)
|
||||||
|
Text(`降采样后大小:${this.afterSampling}`).fontSize(20)
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageKnifeComponent({
|
||||||
|
imageKnifeOption: this.imageKnifeOption
|
||||||
|
})
|
||||||
|
.height(300)
|
||||||
|
.width(300)
|
||||||
|
.borderWidth(1)
|
||||||
|
.borderColor(Color.Pink)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.height('100%')
|
||||||
|
.width('100%')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SamplingType {
|
||||||
|
key: number
|
||||||
|
value: string
|
||||||
|
|
||||||
|
constructor(key: number, value: string) {
|
||||||
|
this.key = key
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,11 @@ struct Index {
|
||||||
uri: 'pages/TestCommonImage',
|
uri: 'pages/TestCommonImage',
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
Button($r('app.string.Image_Downsampling_Functionality')).margin({top:10}).onClick(()=>{
|
||||||
|
router.push({
|
||||||
|
uri: 'pages/DownSamplePage',
|
||||||
|
});
|
||||||
|
})
|
||||||
Button($r('app.string.Test_Task_error')).margin({top:10}).onClick(()=>{
|
Button($r('app.string.Test_Task_error')).margin({top:10}).onClick(()=>{
|
||||||
router.push({
|
router.push({
|
||||||
uri: 'pages/TestTaskResourcePage',
|
uri: 'pages/TestTaskResourcePage',
|
||||||
|
|
|
@ -367,6 +367,10 @@
|
||||||
{
|
{
|
||||||
"name": "TIPS",
|
"name": "TIPS",
|
||||||
"value": "Please shut down the network first and ensure that there is no cache of images from this network in the test failure scenario locally"
|
"value": "Please shut down the network first and ensure that there is no cache of images from this network in the test failure scenario locally"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Image_Downsampling_Functionality",
|
||||||
|
"value": "Downscale Image effect"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -23,6 +23,7 @@
|
||||||
"pages/ImageAnimatorPage",
|
"pages/ImageAnimatorPage",
|
||||||
"pages/TestSetCustomImagePage",
|
"pages/TestSetCustomImagePage",
|
||||||
"pages/TestErrorHolderPage",
|
"pages/TestErrorHolderPage",
|
||||||
"pages/TestTaskResourcePage"
|
"pages/TestTaskResourcePage",
|
||||||
|
"pages/DownSamplePage"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -363,6 +363,10 @@
|
||||||
{
|
{
|
||||||
"name": "TIPS",
|
"name": "TIPS",
|
||||||
"value": "测试失败场景请先关闭网络,并保证本地没有此网络图片的缓存"
|
"value": "测试失败场景请先关闭网络,并保证本地没有此网络图片的缓存"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Image_Downsampling_Functionality",
|
||||||
|
"value": "降采样功能"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -18,7 +18,7 @@ import ImageKnifeOptionTest from './ImageKnifeOption.test';
|
||||||
import MemoryLruCacheTest from './MemoryLruCache.test';
|
import MemoryLruCacheTest from './MemoryLruCache.test';
|
||||||
import ImageKnifeTest from './ImageKnife.test';
|
import ImageKnifeTest from './ImageKnife.test';
|
||||||
import Transform from './transform.test';
|
import Transform from './transform.test';
|
||||||
|
import SamplingTest from './SamplingTest.test';
|
||||||
export default function testsuite() {
|
export default function testsuite() {
|
||||||
MemoryLruCacheTest();
|
MemoryLruCacheTest();
|
||||||
FileLruCacheTest();
|
FileLruCacheTest();
|
||||||
|
@ -26,4 +26,5 @@ export default function testsuite() {
|
||||||
ImageKnifeOptionTest();
|
ImageKnifeOptionTest();
|
||||||
ImageKnifeTest();
|
ImageKnifeTest();
|
||||||
Transform();
|
Transform();
|
||||||
|
SamplingTest()
|
||||||
}
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
|
||||||
|
import { image } from '@kit.ImageKit'
|
||||||
|
import { BusinessError } from '@kit.BasicServicesKit'
|
||||||
|
import { calculateScaleType, Downsampler } from '@ohos/imageknife/src/main/ets/downsampling/Downsampler'
|
||||||
|
import { DownsampleStrategy } from '@ohos/imageknife'
|
||||||
|
|
||||||
|
export default function SamplingTest() {
|
||||||
|
describe('SamplingTest', () => {
|
||||||
|
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
|
||||||
|
beforeAll(() => {
|
||||||
|
// Presets an action, which is performed only once before all test cases of the test suite start.
|
||||||
|
// This API supports only one parameter: preset action function.
|
||||||
|
})
|
||||||
|
beforeEach(() => {
|
||||||
|
// Presets an action, which is performed before each unit test case starts.
|
||||||
|
// The number of execution times is the same as the number of test cases defined by **it**.
|
||||||
|
// This API supports only one parameter: preset action function.
|
||||||
|
})
|
||||||
|
afterEach(() => {
|
||||||
|
// Presets a clear action, which is performed after each unit test case ends.
|
||||||
|
// The number of execution times is the same as the number of test cases defined by **it**.
|
||||||
|
// This API supports only one parameter: clear action function.
|
||||||
|
})
|
||||||
|
afterAll(() => {
|
||||||
|
// Presets a clear action, which is performed after all test cases of the test suite end.
|
||||||
|
// This API supports only one parameter: clear action function.
|
||||||
|
})
|
||||||
|
it('NONE', 0, () => {
|
||||||
|
let reqSize: calculateScaleType =
|
||||||
|
new Downsampler().calculateScaling('jpg', 1024, 1024, 200,
|
||||||
|
200, DownsampleStrategy.NONE,)
|
||||||
|
let req = (reqSize.targetWidth == 1024 && reqSize.targetHeight == 1024)
|
||||||
|
expect(req).assertEqual(true);
|
||||||
|
})
|
||||||
|
it('AT_MOST', 1, () => {
|
||||||
|
let reqSize: calculateScaleType =
|
||||||
|
new Downsampler().calculateScaling('jpg', 1024, 1024, 200,
|
||||||
|
200, DownsampleStrategy.AT_MOST)
|
||||||
|
let req = (reqSize.targetWidth < 1024 && reqSize.targetHeight < 1024)
|
||||||
|
expect(req).assertEqual(true);
|
||||||
|
})
|
||||||
|
it('FIT_CENTER', 2, () => {
|
||||||
|
let reqSize: calculateScaleType =
|
||||||
|
new Downsampler().calculateScaling('jpg', 1024, 1024, 200,
|
||||||
|
200, DownsampleStrategy.FIT_CENTER_MEMORY)
|
||||||
|
let req = (reqSize.targetWidth < 1024 && reqSize.targetHeight < 1024)
|
||||||
|
expect(req).assertEqual(true);
|
||||||
|
})
|
||||||
|
it('CENTER_INSIDE', 3, () => {
|
||||||
|
let reqSize: calculateScaleType =
|
||||||
|
new Downsampler().calculateScaling('jpg', 1024, 1024, 200,
|
||||||
|
200, DownsampleStrategy.CENTER_INSIDE_MEMORY)
|
||||||
|
let req = (reqSize.targetWidth < 1024 && reqSize.targetHeight < 1024)
|
||||||
|
expect(req).assertEqual(true);
|
||||||
|
})
|
||||||
|
it('CENTER_OUTSIDE', 4, () => {
|
||||||
|
let reqSize: calculateScaleType =
|
||||||
|
new Downsampler().calculateScaling('jpg', 1024, 1024, 200,
|
||||||
|
200, DownsampleStrategy.CENTER_OUTSIDE)
|
||||||
|
let req = (reqSize.targetWidth < 1024 && reqSize.targetHeight < 1024)
|
||||||
|
expect(req).assertEqual(true);
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
|
@ -66,4 +66,6 @@ export { CropTransformation } from './src/main/ets/transform/CropTransformation'
|
||||||
|
|
||||||
export { MaskTransformation } from './src/main/ets/transform/MaskTransformation'
|
export { MaskTransformation } from './src/main/ets/transform/MaskTransformation'
|
||||||
|
|
||||||
export { SepiaTransformation } from './src/main/ets/transform/SepiaTransformation'
|
export { SepiaTransformation } from './src/main/ets/transform/SepiaTransformation'
|
||||||
|
|
||||||
|
export { DownsampleStrategy } from './src/main/ets/downsampling/DownsampleStartegy'
|
|
@ -35,6 +35,7 @@ import {
|
||||||
} from './model/ImageKnifeData'
|
} from './model/ImageKnifeData'
|
||||||
import { BusinessError } from '@kit.BasicServicesKit';
|
import { BusinessError } from '@kit.BasicServicesKit';
|
||||||
import { ImageKnifeLoader } from './ImageKnifeLoader'
|
import { ImageKnifeLoader } from './ImageKnifeLoader'
|
||||||
|
import { DownsampleStrategy } from './downsampling/DownsampleStartegy';
|
||||||
|
|
||||||
|
|
||||||
export class ImageKnifeDispatcher {
|
export class ImageKnifeDispatcher {
|
||||||
|
@ -183,7 +184,10 @@ export class ImageKnifeDispatcher {
|
||||||
fileCacheFolder: ImageKnife.getInstance().getFileCache()?.getCacheFolder(),
|
fileCacheFolder: ImageKnife.getInstance().getFileCache()?.getCacheFolder(),
|
||||||
isAnimator:isAnimator,
|
isAnimator:isAnimator,
|
||||||
moduleName: moduleName == "" ? undefined : moduleName,
|
moduleName: moduleName == "" ? undefined : moduleName,
|
||||||
resName: resName == "" ? undefined : resName
|
resName: resName == "" ? undefined : resName,
|
||||||
|
targetWidth: currentRequest.componentWidth,
|
||||||
|
targetHeight: currentRequest.componentHeight,
|
||||||
|
downsampType: currentRequest.imageKnifeOption.downsampleOf==undefined?DownsampleStrategy.NONE:currentRequest.imageKnifeOption.downsampleOf,
|
||||||
}
|
}
|
||||||
|
|
||||||
if(request.customGetImage == undefined) {
|
if(request.customGetImage == undefined) {
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
import {
|
import {
|
||||||
CacheStrategy,
|
CacheStrategy,
|
||||||
ImageKnifeRequestSource,
|
ImageKnifeRequestSource,
|
||||||
ImageKnifeRequestWithSource, RequestJobRequest } from './model/ImageKnifeData';
|
ImageKnifeRequestWithSource,
|
||||||
|
RequestJobRequest
|
||||||
|
} from './model/ImageKnifeData';
|
||||||
import List from '@ohos.util.List'
|
import List from '@ohos.util.List'
|
||||||
import { FileCache } from './cache/FileCache';
|
import { FileCache } from './cache/FileCache';
|
||||||
import { LogUtil } from './utils/LogUtil';
|
import { LogUtil } from './utils/LogUtil';
|
||||||
|
@ -28,6 +30,8 @@ import emitter from '@ohos.events.emitter';
|
||||||
import image from '@ohos.multimedia.image';
|
import image from '@ohos.multimedia.image';
|
||||||
import { RequestJobResult } from './model/ImageKnifeData'
|
import { RequestJobResult } from './model/ImageKnifeData'
|
||||||
import util from '@ohos.util';
|
import util from '@ohos.util';
|
||||||
|
import { DownsampleStrategy } from './downsampling/DownsampleStartegy';
|
||||||
|
import { Downsampler } from './downsampling/Downsampler';
|
||||||
|
|
||||||
class RequestData {
|
class RequestData {
|
||||||
receiveSize: number = 2000
|
receiveSize: number = 2000
|
||||||
|
@ -40,20 +44,20 @@ class RequestData {
|
||||||
export class ImageKnifeLoader {
|
export class ImageKnifeLoader {
|
||||||
static async parseImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string,
|
static async parseImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string,
|
||||||
request: RequestJobRequest): Promise<RequestJobResult> {
|
request: RequestJobRequest): Promise<RequestJobResult> {
|
||||||
if(request.isAnimator) {
|
if (request.isAnimator) {
|
||||||
return ImageKnifeLoader.parseForAnimatorComponent(resBuf ,typeValue ,fileKey, request)
|
return ImageKnifeLoader.parseForAnimatorComponent(resBuf, typeValue, fileKey, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeValue === 'gif' || typeValue === 'webp') {
|
if (typeValue === 'gif' || typeValue === 'webp') {
|
||||||
return ImageKnifeLoader.parseAnimatorImage(resBuf ,typeValue ,fileKey , request)
|
return ImageKnifeLoader.parseAnimatorImage(resBuf, typeValue, fileKey, request)
|
||||||
} else if(typeValue == "svg") {
|
} else if (typeValue == "svg") {
|
||||||
return ImageKnifeLoader.parseSvgImage(resBuf ,typeValue ,fileKey , request)
|
return ImageKnifeLoader.parseSvgImage(resBuf, typeValue, fileKey, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request)
|
return ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
static makeEmptyResult(error: string): RequestJobResult{
|
static makeEmptyResult(error: string): RequestJobResult {
|
||||||
return {
|
return {
|
||||||
pixelMap: undefined,
|
pixelMap: undefined,
|
||||||
bufferSize: 0,
|
bufferSize: 0,
|
||||||
|
@ -62,17 +66,28 @@ export class ImageKnifeLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async parseNormalImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, request: RequestJobRequest):Promise<RequestJobResult> {
|
static async parseNormalImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string,
|
||||||
|
request: RequestJobRequest): Promise<RequestJobResult> {
|
||||||
let resPixelmap: PixelMap | undefined = undefined
|
let resPixelmap: PixelMap | undefined = undefined
|
||||||
let decodingOptions: image.DecodingOptions = {
|
let decodingOptions: image.DecodingOptions = {
|
||||||
editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true : false,
|
editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true :
|
||||||
|
false,
|
||||||
}
|
}
|
||||||
let imageSource: image.ImageSource = image.createImageSource(resBuf)
|
let imageSource: image.ImageSource = image.createImageSource(resBuf)
|
||||||
if (imageSource === undefined){
|
if (imageSource === undefined) {
|
||||||
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed")
|
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = (await imageSource.getImageInfo()).size
|
let size = (await imageSource.getImageInfo()).size
|
||||||
|
try {
|
||||||
|
if ((request.downsampType !== DownsampleStrategy.NONE) &&
|
||||||
|
request.requestSource == ImageKnifeRequestSource.SRC) {
|
||||||
|
decodingOptions = ImageKnifeLoader.downsamplerReqSize(typeValue, request, size, ImageKnifeRequestSource.SRC)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return ImageKnifeLoader.makeEmptyResult(err)
|
||||||
|
}
|
||||||
|
|
||||||
await imageSource.createPixelMap(decodingOptions)
|
await imageSource.createPixelMap(decodingOptions)
|
||||||
.then((pixelmap: PixelMap) => {
|
.then((pixelmap: PixelMap) => {
|
||||||
resPixelmap = pixelmap
|
resPixelmap = pixelmap
|
||||||
|
@ -86,15 +101,16 @@ export class ImageKnifeLoader {
|
||||||
pixelMap: resPixelmap,
|
pixelMap: resPixelmap,
|
||||||
bufferSize: resBuf.byteLength,
|
bufferSize: resBuf.byteLength,
|
||||||
fileKey: fileKey,
|
fileKey: fileKey,
|
||||||
size:size,
|
size: size,
|
||||||
type:typeValue
|
type: typeValue
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static async parseSvgImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string,
|
static async parseSvgImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string,
|
||||||
request: RequestJobRequest): Promise<RequestJobResult> {
|
request: RequestJobRequest): Promise<RequestJobResult> {
|
||||||
let resPixelmap: PixelMap | undefined = undefined
|
let resPixelmap: PixelMap | undefined = undefined
|
||||||
let imageSource: image.ImageSource = image.createImageSource(resBuf)
|
let imageSource: image.ImageSource = image.createImageSource(resBuf)
|
||||||
if (imageSource === undefined){
|
if (imageSource === undefined) {
|
||||||
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed")
|
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +126,14 @@ export class ImageKnifeLoader {
|
||||||
editable: true,
|
editable: true,
|
||||||
desiredSize: defaultSize
|
desiredSize: defaultSize
|
||||||
};
|
};
|
||||||
|
try {
|
||||||
|
if ((request.downsampType !== DownsampleStrategy.NONE) &&
|
||||||
|
request.requestSource == ImageKnifeRequestSource.SRC) {
|
||||||
|
opts = ImageKnifeLoader.downsamplerReqSize(typeValue, request, size)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return ImageKnifeLoader.makeEmptyResult(err)
|
||||||
|
}
|
||||||
await imageSource.createPixelMap(opts)
|
await imageSource.createPixelMap(opts)
|
||||||
.then((pixelmap: PixelMap) => {
|
.then((pixelmap: PixelMap) => {
|
||||||
resPixelmap = pixelmap
|
resPixelmap = pixelmap
|
||||||
|
@ -123,13 +147,14 @@ export class ImageKnifeLoader {
|
||||||
pixelMap: resPixelmap,
|
pixelMap: resPixelmap,
|
||||||
bufferSize: resBuf.byteLength,
|
bufferSize: resBuf.byteLength,
|
||||||
fileKey: fileKey,
|
fileKey: fileKey,
|
||||||
type:typeValue
|
type: typeValue
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static async parseAnimatorImage(resBuf: ArrayBuffer, typeValue: string,
|
static async parseAnimatorImage(resBuf: ArrayBuffer, typeValue: string,
|
||||||
fileKey: string,request: RequestJobRequest): Promise<RequestJobResult> {
|
fileKey: string, request: RequestJobRequest): Promise<RequestJobResult> {
|
||||||
let imageSource: image.ImageSource = image.createImageSource(resBuf)
|
let imageSource: image.ImageSource = image.createImageSource(resBuf)
|
||||||
if (imageSource === undefined){
|
if (imageSource === undefined) {
|
||||||
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed")
|
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,28 +162,32 @@ export class ImageKnifeLoader {
|
||||||
let size = (await imageSource.getImageInfo()).size
|
let size = (await imageSource.getImageInfo()).size
|
||||||
imageSource.release()
|
imageSource.release()
|
||||||
|
|
||||||
if(frameCount == undefined || frameCount == 1) {
|
if (frameCount == undefined || frameCount == 1) {
|
||||||
} else {
|
} else {
|
||||||
let base64str = "data:image/" + typeValue + ";base64," + new util.Base64Helper().encodeToStringSync(new Uint8Array(resBuf))
|
let base64str =
|
||||||
|
"data:image/" + typeValue + ";base64," + new util.Base64Helper().encodeToStringSync(new Uint8Array(resBuf))
|
||||||
return {
|
return {
|
||||||
pixelMap: base64str,
|
pixelMap: base64str,
|
||||||
bufferSize: resBuf.byteLength,
|
bufferSize: resBuf.byteLength,
|
||||||
fileKey: fileKey,
|
fileKey: fileKey,
|
||||||
size:size,
|
size: size,
|
||||||
type:typeValue
|
type: typeValue
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request)
|
return ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为AnimatorComponent解析动图
|
// 为AnimatorComponent解析动图
|
||||||
static async parseForAnimatorComponent(resBuf: ArrayBuffer, typeValue: string, fileKey: string,request: RequestJobRequest): Promise<RequestJobResult> {
|
static async parseForAnimatorComponent(resBuf: ArrayBuffer, typeValue: string, fileKey: string,
|
||||||
|
request: RequestJobRequest): Promise<RequestJobResult> {
|
||||||
if (typeValue === 'gif' || typeValue === 'webp') {
|
if (typeValue === 'gif' || typeValue === 'webp') {
|
||||||
let imageSource: image.ImageSource = image.createImageSource(resBuf);
|
let imageSource: image.ImageSource = image.createImageSource(resBuf);
|
||||||
if (imageSource === undefined){
|
if (imageSource === undefined) {
|
||||||
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed")
|
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed")
|
||||||
}
|
}
|
||||||
let decodingOptions: image.DecodingOptions = {
|
let decodingOptions: image.DecodingOptions = {
|
||||||
editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true : false,
|
editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true :
|
||||||
|
false,
|
||||||
}
|
}
|
||||||
let pixelMapList: Array<PixelMap> = []
|
let pixelMapList: Array<PixelMap> = []
|
||||||
let delayList: Array<number> = []
|
let delayList: Array<number> = []
|
||||||
|
@ -195,13 +224,15 @@ export class ImageKnifeLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取图片资源
|
// 获取图片资源
|
||||||
static async getImageArrayBuffer(request: RequestJobRequest, requestList: List<ImageKnifeRequestWithSource> | undefined,fileKey:string): Promise<ArrayBuffer> {
|
static async getImageArrayBuffer(request: RequestJobRequest,
|
||||||
|
requestList: List<ImageKnifeRequestWithSource> | undefined, fileKey: string): Promise<ArrayBuffer> {
|
||||||
let resBuf: ArrayBuffer | undefined
|
let resBuf: ArrayBuffer | undefined
|
||||||
|
|
||||||
// 判断自定义下载
|
// 判断自定义下载
|
||||||
if (request.customGetImage !== undefined && request.requestSource == ImageKnifeRequestSource.SRC && typeof request.src == "string") {
|
if (request.customGetImage !== undefined && request.requestSource == ImageKnifeRequestSource.SRC &&
|
||||||
|
typeof request.src == "string") {
|
||||||
// 先从文件缓存获取
|
// 先从文件缓存获取
|
||||||
resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder)
|
resBuf = FileCache.getFileCacheByFile(request.context, fileKey, request.fileCacheFolder)
|
||||||
if (resBuf === undefined) {
|
if (resBuf === undefined) {
|
||||||
LogUtil.log("start customGetImage src=" + request.src)
|
LogUtil.log("start customGetImage src=" + request.src)
|
||||||
try {
|
try {
|
||||||
|
@ -220,16 +251,14 @@ export class ImageKnifeLoader {
|
||||||
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:" + request.src)
|
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:" + request.src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (typeof request.src === 'string') {
|
if (typeof request.src === 'string') {
|
||||||
if (request.src.indexOf("http://") == 0 || request.src.indexOf("https://") == 0) { //从网络下载
|
if (request.src.indexOf("http://") == 0 || request.src.indexOf("https://") == 0) { //从网络下载
|
||||||
// 先从文件缓存获取
|
// 先从文件缓存获取
|
||||||
resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder)
|
resBuf = FileCache.getFileCacheByFile(request.context, fileKey, request.fileCacheFolder)
|
||||||
if (resBuf !== undefined){
|
if (resBuf !== undefined) {
|
||||||
LogUtil.log("success get image from filecache for key = " + fileKey + " src = " + request.src)
|
LogUtil.log("success get image from filecache for key = " + fileKey + " src = " + request.src)
|
||||||
}
|
} else if (request.onlyRetrieveFromCache != true) {
|
||||||
else if (request.onlyRetrieveFromCache != true) {
|
|
||||||
LogUtil.log("HttpDownloadClient.start:" + request.src)
|
LogUtil.log("HttpDownloadClient.start:" + request.src)
|
||||||
let httpRequest = http.createHttp();
|
let httpRequest = http.createHttp();
|
||||||
let progress: number = 0
|
let progress: number = 0
|
||||||
|
@ -258,10 +287,11 @@ export class ImageKnifeLoader {
|
||||||
if (requestList === undefined) {
|
if (requestList === undefined) {
|
||||||
// 子线程
|
// 子线程
|
||||||
emitter.emit(Constants.PROGRESS_EMITTER + request.memoryKey, { data: { "value": progress } })
|
emitter.emit(Constants.PROGRESS_EMITTER + request.memoryKey, { data: { "value": progress } })
|
||||||
}else {
|
} else {
|
||||||
// 主线程请求
|
// 主线程请求
|
||||||
requestList!.forEach((requestWithSource: ImageKnifeRequestWithSource) => {
|
requestList!.forEach((requestWithSource: ImageKnifeRequestWithSource) => {
|
||||||
if (requestWithSource.request.imageKnifeOption.progressListener !== undefined && requestWithSource.source === ImageKnifeRequestSource.SRC) {
|
if (requestWithSource.request.imageKnifeOption.progressListener !== undefined &&
|
||||||
|
requestWithSource.source === ImageKnifeRequestSource.SRC) {
|
||||||
requestWithSource.request.imageKnifeOption.progressListener(progress)
|
requestWithSource.request.imageKnifeOption.progressListener(progress)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -292,29 +322,31 @@ export class ImageKnifeLoader {
|
||||||
LogUtil.log("HttpDownloadClient.end:" + request.src)
|
LogUtil.log("HttpDownloadClient.end:" + request.src)
|
||||||
// 保存文件缓存
|
// 保存文件缓存
|
||||||
if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) {
|
if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) {
|
||||||
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:"+request.src)
|
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:" + request.src)
|
||||||
FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf , request.fileCacheFolder)
|
FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf, request.fileCacheFolder)
|
||||||
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:"+request.src)
|
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:" + request.src)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
throw new Error('onlyRetrieveFromCache,do not fetch image src = ' + request.src)
|
throw new Error('onlyRetrieveFromCache,do not fetch image src = ' + request.src)
|
||||||
}
|
}
|
||||||
} else if (request.src.startsWith('datashare://') || request.src.startsWith('file://')) {
|
} else if (request.src.startsWith('datashare://') || request.src.startsWith('file://')) {
|
||||||
await fs.open(request.src, fs.OpenMode.READ_ONLY).then(async (file) => {
|
await fs.open(request.src, fs.OpenMode.READ_ONLY).then(async (file) => {
|
||||||
await fs.stat(file.fd).then(async (stat) =>{
|
await fs.stat(file.fd).then(async (stat) => {
|
||||||
let buf = new ArrayBuffer(stat.size);
|
let buf = new ArrayBuffer(stat.size);
|
||||||
await fs.read(file.fd, buf).then((readLen) => {
|
await fs.read(file.fd, buf).then((readLen) => {
|
||||||
resBuf = buf;
|
resBuf = buf;
|
||||||
fs.closeSync(file.fd);
|
fs.closeSync(file.fd);
|
||||||
}).catch((err:BusinessError) => {
|
}).catch((err: BusinessError) => {
|
||||||
throw new Error('LoadDataShareFileClient fs.read err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code)
|
throw new Error('LoadDataShareFileClient fs.read err happened uri=' + request.src + " err.msg=" +
|
||||||
|
err?.message + " err.code=" + err?.code)
|
||||||
})
|
})
|
||||||
}).catch((err:BusinessError) => {
|
}).catch((err: BusinessError) => {
|
||||||
throw new Error('LoadDataShareFileClient fs.stat err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code)
|
throw new Error('LoadDataShareFileClient fs.stat err happened uri=' + request.src + " err.msg=" +
|
||||||
|
err?.message + " err.code=" + err?.code)
|
||||||
})
|
})
|
||||||
}).catch((err:BusinessError) => {
|
}).catch((err: BusinessError) => {
|
||||||
throw new Error('LoadDataShareFileClient fs.open err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code)
|
throw new Error('LoadDataShareFileClient fs.open err happened uri=' + request.src + " err.msg=" +
|
||||||
|
err?.message + " err.code=" + err?.code)
|
||||||
})
|
})
|
||||||
} else { //从本地文件获取
|
} else { //从本地文件获取
|
||||||
try {
|
try {
|
||||||
|
@ -331,15 +363,16 @@ export class ImageKnifeLoader {
|
||||||
}
|
}
|
||||||
} else if (typeof request.src == "number") { //从资源文件获取
|
} else if (typeof request.src == "number") { //从资源文件获取
|
||||||
let manager = request.context.createModuleContext(request.moduleName).resourceManager
|
let manager = request.context.createModuleContext(request.moduleName).resourceManager
|
||||||
if (resBuf == undefined && request.onlyRetrieveFromCache != true && request.requestSource == ImageKnifeRequestSource.SRC) {
|
if (resBuf == undefined && request.onlyRetrieveFromCache != true &&
|
||||||
if(request.src == -1) {
|
request.requestSource == ImageKnifeRequestSource.SRC) {
|
||||||
|
if (request.src == -1) {
|
||||||
let resName = request.resName as string
|
let resName = request.resName as string
|
||||||
resBuf = (await manager.getMediaByName(resName.substring(10))).buffer as ArrayBuffer
|
resBuf = (await manager.getMediaByName(resName.substring(10))).buffer as ArrayBuffer
|
||||||
} else {
|
} else {
|
||||||
resBuf = manager.getMediaContentSync(request.src).buffer as ArrayBuffer
|
resBuf = manager.getMediaContentSync(request.src).buffer as ArrayBuffer
|
||||||
}
|
}
|
||||||
} else if (resBuf == undefined && request.requestSource != ImageKnifeRequestSource.SRC) {
|
} else if (resBuf == undefined && request.requestSource != ImageKnifeRequestSource.SRC) {
|
||||||
if(request.src == -1) {
|
if (request.src == -1) {
|
||||||
let resName = request.resName as string
|
let resName = request.resName as string
|
||||||
resBuf = (await manager.getMediaByName(resName.substring(10))).buffer as ArrayBuffer
|
resBuf = (await manager.getMediaByName(resName.substring(10))).buffer as ArrayBuffer
|
||||||
} else {
|
} else {
|
||||||
|
@ -349,9 +382,35 @@ export class ImageKnifeLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resBuf === undefined){
|
if (resBuf === undefined) {
|
||||||
throw new Error('getImageArrayBuffer undefined')
|
throw new Error('getImageArrayBuffer undefined')
|
||||||
}
|
}
|
||||||
return resBuf
|
return resBuf
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static downsamplerReqSize(typeValue: string, request: RequestJobRequest, size: Size,
|
||||||
|
SRC?: ImageKnifeRequestSource) {
|
||||||
|
let reqSize =
|
||||||
|
new Downsampler().calculateScaling(typeValue, size.width, size.height, request.targetWidth, request.targetHeight,
|
||||||
|
request.downsampType)
|
||||||
|
if (typeValue == "svg") {
|
||||||
|
return ({
|
||||||
|
editable: true,
|
||||||
|
desiredSize: ({
|
||||||
|
height: vp2px(reqSize.height),
|
||||||
|
width: vp2px(reqSize.width)
|
||||||
|
} as Size)
|
||||||
|
|
||||||
|
} as image.DecodingOptions)
|
||||||
|
} else {
|
||||||
|
return ({
|
||||||
|
editable: request.requestSource === SRC && request.transformation !== undefined ? true : false,
|
||||||
|
desiredSize: ({
|
||||||
|
width: reqSize.width,
|
||||||
|
height: reqSize.height
|
||||||
|
} as Size)
|
||||||
|
} as image.DecodingOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ export struct ImageKnifeComponent {
|
||||||
@Param imageKnifeOption: ImageKnifeOption = new ImageKnifeOption();
|
@Param imageKnifeOption: ImageKnifeOption = new ImageKnifeOption();
|
||||||
|
|
||||||
@Monitor('imageKnifeOption',
|
@Monitor('imageKnifeOption',
|
||||||
"imageKnifeOption.loadSrc","imageKnifeOption.signature","imageKnifeOption.transformation","imageKnifeOption.border","imageKnifeOption.objectFit")
|
"imageKnifeOption.loadSrc","imageKnifeOption.signature","imageKnifeOption.transformation","imageKnifeOption.border","imageKnifeOption.objectFit",'imageKnifeOption.downsampleOf')
|
||||||
watchImageKnifeOption() {
|
watchImageKnifeOption() {
|
||||||
this.clearLastRequest()
|
this.clearLastRequest()
|
||||||
this.componentVersion++
|
this.componentVersion++
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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 { DownsampleStrategy } from './DownsampleStartegy';
|
||||||
|
|
||||||
|
import { SampleSizeRounding } from './DownsampleUtils';
|
||||||
|
|
||||||
|
export interface BaseDownsampling {
|
||||||
|
getName(): string
|
||||||
|
|
||||||
|
getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number,downsampType?:DownsampleStrategy): number
|
||||||
|
//
|
||||||
|
getSampleSizeType(sourceWidth: number, sourceHeight: number, requestWidth: number,
|
||||||
|
requestHeight: number,downsampType?:DownsampleStrategy): SampleSizeRounding
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
/*
|
||||||
|
* 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 { BaseDownsampling } from './BaseDownsampling';
|
||||||
|
import { getScale, highestOneBit, round, SampleSizeRounding } from './DownsampleUtils';
|
||||||
|
|
||||||
|
export class FitCenter implements BaseDownsampling {
|
||||||
|
getName() {
|
||||||
|
return "FitCenter"
|
||||||
|
}
|
||||||
|
|
||||||
|
getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
||||||
|
downsampType: DownsampleStrategy
|
||||||
|
): number {
|
||||||
|
//重新计算宽高比;
|
||||||
|
let outSize: Size = {
|
||||||
|
width: round(getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType) * sourceWidth),
|
||||||
|
height:round(getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType) * sourceHeight)
|
||||||
|
}
|
||||||
|
let scaleFactor = this.getSampleSizeType(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)===
|
||||||
|
SampleSizeRounding.QUALITY? Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height))) :
|
||||||
|
Math.max(1, highestOneBit(Math.min(sourceWidth / outSize.width, sourceHeight / outSize.height)))//将整型的缩放因子转换为2的次幂采样大小
|
||||||
|
|
||||||
|
if (this.getSampleSizeType(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)===
|
||||||
|
SampleSizeRounding.MEMORY && (scaleFactor < (1 / getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)))) {
|
||||||
|
scaleFactor = scaleFactor << 1;
|
||||||
|
}
|
||||||
|
return scaleFactor
|
||||||
|
}
|
||||||
|
|
||||||
|
getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
||||||
|
downsampType: DownsampleStrategy
|
||||||
|
): number {
|
||||||
|
if (downsampType === DownsampleStrategy.FIT_CENTER_QUALITY) {
|
||||||
|
return SampleSizeRounding.QUALITY;
|
||||||
|
} else {
|
||||||
|
return SampleSizeRounding.MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class None implements BaseDownsampling {
|
||||||
|
getName(): string {
|
||||||
|
return "DownsampleNone"
|
||||||
|
}
|
||||||
|
|
||||||
|
public getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number,
|
||||||
|
requestedHeight: number): number {
|
||||||
|
let outSize: Size = {
|
||||||
|
width: round(sourceWidth),
|
||||||
|
height:round(sourceHeight)
|
||||||
|
}
|
||||||
|
let scaleFactor =
|
||||||
|
Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height)))
|
||||||
|
return scaleFactor
|
||||||
|
}
|
||||||
|
|
||||||
|
//实现 getSampleSizeType 方法
|
||||||
|
public getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number,
|
||||||
|
requestedHeight: number): SampleSizeRounding {
|
||||||
|
return SampleSizeRounding.QUALITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*宽高进行等比缩放宽高里面最小的比例先放进去
|
||||||
|
然后再更据原图的缩放比去适配另一边*/
|
||||||
|
|
||||||
|
export class CenterOutside implements BaseDownsampling {
|
||||||
|
getName() {
|
||||||
|
return "CenterOutside"
|
||||||
|
}
|
||||||
|
|
||||||
|
getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number {
|
||||||
|
const widthPercentage = requestedWidth / sourceWidth;
|
||||||
|
const heightPercentage = requestedHeight / sourceHeight;
|
||||||
|
//返回宽度和高度比例中最大的值
|
||||||
|
let outSize: Size = {
|
||||||
|
width: round(Math.max(widthPercentage, heightPercentage) * sourceWidth),
|
||||||
|
height:round(Math.max(widthPercentage, heightPercentage) * sourceHeight)
|
||||||
|
}
|
||||||
|
let scaleFactor = Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height)))
|
||||||
|
|
||||||
|
return scaleFactor
|
||||||
|
}
|
||||||
|
|
||||||
|
getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number,
|
||||||
|
requestedHeight: number): SampleSizeRounding {
|
||||||
|
return SampleSizeRounding.QUALITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*请求尺寸大于实际尺寸不进行放大,按照原图展示*/
|
||||||
|
|
||||||
|
export class AtMost implements BaseDownsampling {
|
||||||
|
getName() {
|
||||||
|
return "AtMost"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
||||||
|
greaterOrEqualSampleSize = lesserOrEqualSampleSize <<= 1;
|
||||||
|
}
|
||||||
|
greaterOrEqualSampleSize = lesserOrEqualSampleSize << (lesserOrEqualSampleSize < maxIntegerFactor ? 1 : 0)
|
||||||
|
//返回缩放因子
|
||||||
|
let outSize: Size = {
|
||||||
|
width: round((1 / greaterOrEqualSampleSize) * sourceWidth),
|
||||||
|
height:round((1 / greaterOrEqualSampleSize) * sourceHeight)
|
||||||
|
}
|
||||||
|
let scaleFactor = Math.max(1, highestOneBit(Math.min(sourceWidth / outSize.width, sourceHeight / outSize.height)))
|
||||||
|
if ((scaleFactor < greaterOrEqualSampleSize)) {
|
||||||
|
scaleFactor = scaleFactor << 1;
|
||||||
|
}
|
||||||
|
return scaleFactor
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number,
|
||||||
|
requestHeight: number): SampleSizeRounding {
|
||||||
|
//根据 AtMost 的逻辑,总是返回 MEMORY
|
||||||
|
return SampleSizeRounding.MEMORY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*宽高进行等比缩放宽高里面最大的比例先放进去
|
||||||
|
然后再更据原图的缩放比去适配另一边*/
|
||||||
|
export class CenterInside implements BaseDownsampling {
|
||||||
|
getName() {
|
||||||
|
return "CenterInside"
|
||||||
|
}
|
||||||
|
getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
||||||
|
downsampType: DownsampleStrategy
|
||||||
|
): number {
|
||||||
|
|
||||||
|
let outSize: Size = {
|
||||||
|
width: round(Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)) * sourceWidth),
|
||||||
|
height:round(Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)) * sourceHeight)
|
||||||
|
}
|
||||||
|
//将整型的缩放因子转换为2的次幂采样大小
|
||||||
|
let scaleFactor = this.getSampleSizeType(sourceWidth, sourceHeight, requestedWidth, requestedHeight,
|
||||||
|
downsampType) == SampleSizeRounding.QUALITY ?
|
||||||
|
Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height))) :
|
||||||
|
Math.max(1, highestOneBit(Math.min(sourceWidth / outSize.width, sourceHeight / outSize.height)))
|
||||||
|
if (this.getSampleSizeType(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)
|
||||||
|
== SampleSizeRounding.MEMORY && (scaleFactor < (1 / Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType))))) {
|
||||||
|
scaleFactor = scaleFactor << 1;
|
||||||
|
}
|
||||||
|
return scaleFactor
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
||||||
|
downsampType: DownsampleStrategy
|
||||||
|
): SampleSizeRounding {
|
||||||
|
//如果缩放因子为 1,表示没有缩放,优先选择质量
|
||||||
|
if (Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)) === 1) {
|
||||||
|
return SampleSizeRounding.QUALITY
|
||||||
|
}
|
||||||
|
//否则,使用 FIL_CENTER 的 SampleSizeRounding 值
|
||||||
|
return this.getSampleSize(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSampleSize(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
||||||
|
downsampType: DownsampleStrategy
|
||||||
|
): SampleSizeRounding {
|
||||||
|
if (downsampType === DownsampleStrategy.CENTER_INSIDE_MEMORY) {
|
||||||
|
return SampleSizeRounding.MEMORY;
|
||||||
|
} else {
|
||||||
|
return SampleSizeRounding.QUALITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DownsampleStrategy {
|
||||||
|
//请求尺寸大于实际尺寸不进行放大
|
||||||
|
AT_MOST,
|
||||||
|
//两边自适应内存优先
|
||||||
|
FIT_CENTER_MEMORY,
|
||||||
|
//两边自适应质量优先
|
||||||
|
FIT_CENTER_QUALITY,
|
||||||
|
//按照宽高比的最大比进行适配内存优先
|
||||||
|
CENTER_INSIDE_MEMORY,
|
||||||
|
//按照宽高比的最大比进行适配质量优先
|
||||||
|
CENTER_INSIDE_QUALITY,
|
||||||
|
//宽高进行等比缩放宽高里面最小的比例先放进去,然后再根据原图的缩放比去适配
|
||||||
|
CENTER_OUTSIDE,
|
||||||
|
//不进行降采样
|
||||||
|
NONE,
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* 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 { DownsampleStrategy } from './DownsampleStartegy';
|
||||||
|
|
||||||
|
export enum SampleSizeRounding {
|
||||||
|
/**
|
||||||
|
* Prefer to round the sample size up so that the image is downsampled to smaller than the
|
||||||
|
* requested size to use less memory.
|
||||||
|
*/
|
||||||
|
//(内存优先)
|
||||||
|
MEMORY,
|
||||||
|
/**
|
||||||
|
* Prefer to round the sample size down so that the image is downsampled to larger than the
|
||||||
|
* requested size to maintain quality at the expense of extra memory usage.
|
||||||
|
*/
|
||||||
|
//(质量优先)
|
||||||
|
QUALITY
|
||||||
|
}
|
||||||
|
|
||||||
|
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 function getScale(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
|
||||||
|
downsampType: DownsampleStrategy
|
||||||
|
): number {
|
||||||
|
if (downsampType === DownsampleStrategy.FIT_CENTER_MEMORY) {
|
||||||
|
const widthPercentage = requestedWidth / sourceWidth
|
||||||
|
const heightPercentage = requestedHeight / sourceHeight
|
||||||
|
return Math.min(widthPercentage, heightPercentage)
|
||||||
|
} else {
|
||||||
|
const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth);
|
||||||
|
return maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function round(value: number): number {
|
||||||
|
return Math.floor(value + 0.5);
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
AtMost,
|
||||||
|
CenterInside,
|
||||||
|
CenterOutside,
|
||||||
|
DownsampleStrategy,
|
||||||
|
FitCenter,
|
||||||
|
None,
|
||||||
|
} from './DownsampleStartegy';
|
||||||
|
import { highestOneBit, round, SampleSizeRounding } from './DownsampleUtils';
|
||||||
|
|
||||||
|
|
||||||
|
export class Downsampler {
|
||||||
|
calculateScaling(
|
||||||
|
typeValue: string,
|
||||||
|
sourceWidth: number, //原始宽高
|
||||||
|
sourceHeight: number, //原始宽高
|
||||||
|
requestWidth: number, //请求宽高
|
||||||
|
requestHeight: number, //请求宽高
|
||||||
|
downsampType: DownsampleStrategy,
|
||||||
|
): Size {
|
||||||
|
|
||||||
|
if (sourceHeight <= 0 || sourceWidth <= 0) {
|
||||||
|
throw new Error(`Invalid width and height, sourceHeight:${sourceHeight}+ sourceWidth:${sourceWidth}`)
|
||||||
|
}
|
||||||
|
let downsampler = this.getDownsampler(downsampType);
|
||||||
|
let scaleFactor: number =
|
||||||
|
downsampler.getScaleFactor(sourceWidth, sourceHeight, requestWidth, requestHeight, downsampType);//缩放比
|
||||||
|
// let rounding: SampleSizeRounding =
|
||||||
|
// downsampler.getSampleSizeType(sourceWidth, sourceHeight, requestWidth, requestHeight, downsampType); //采样类型
|
||||||
|
//根据缩放比重新计算宽高
|
||||||
|
// let outSize: Size = {
|
||||||
|
// width: round(exactScaleFactor * sourceWidth),
|
||||||
|
// height:round(exactScaleFactor * sourceHeight)
|
||||||
|
// }
|
||||||
|
// let scaleFactor =
|
||||||
|
// rounding == SampleSizeRounding.QUALITY ? Math.max(sourceWidth / exactScaleFactor.width, sourceHeight / exactScaleFactor.height) :
|
||||||
|
// Math.min(sourceWidth / exactScaleFactor.width, sourceHeight / exactScaleFactor.height) //重新计算宽高比
|
||||||
|
|
||||||
|
// scaleFactor = Math.max(1, highestOneBit(scaleFactor))//将整型的缩放因子转换为2的次幂采样大小
|
||||||
|
//
|
||||||
|
// if (rounding == 0 && (scaleFactor < (1 / exactScaleFactor))) {
|
||||||
|
// scaleFactor = scaleFactor << 1;
|
||||||
|
// }
|
||||||
|
//基于上一步得出的采样大小,根据不同的图片类型,计算采样后的图片尺寸
|
||||||
|
if (typeValue === "png") {
|
||||||
|
return {
|
||||||
|
width: Math.floor(sourceWidth / scaleFactor),
|
||||||
|
height: Math.floor(sourceHeight / scaleFactor)
|
||||||
|
}
|
||||||
|
} else if (typeValue === "webp") {
|
||||||
|
return {
|
||||||
|
width: Math.round(sourceWidth / scaleFactor),
|
||||||
|
height: Math.round(sourceHeight / scaleFactor)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
width: sourceWidth / scaleFactor,
|
||||||
|
height: sourceHeight / scaleFactor
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDownsampler(downsampType: DownsampleStrategy) {
|
||||||
|
switch (downsampType) {
|
||||||
|
case DownsampleStrategy.FIT_CENTER_MEMORY:
|
||||||
|
case DownsampleStrategy.FIT_CENTER_QUALITY:
|
||||||
|
return new FitCenter();
|
||||||
|
case DownsampleStrategy.NONE:
|
||||||
|
return new None();
|
||||||
|
case DownsampleStrategy.AT_MOST:
|
||||||
|
return new AtMost();
|
||||||
|
case DownsampleStrategy.CENTER_INSIDE_MEMORY:
|
||||||
|
case DownsampleStrategy.CENTER_INSIDE_QUALITY:
|
||||||
|
return new CenterInside();
|
||||||
|
case DownsampleStrategy.CENTER_OUTSIDE:
|
||||||
|
return new CenterOutside();
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported downsampling strategy');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ import { ImageKnifeOption } from '../model/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 {
|
||||||
|
@ -31,6 +32,9 @@ export class DefaultEngineKey implements IEngineKey {
|
||||||
if (imageKnifeOption.transformation) {
|
if (imageKnifeOption.transformation) {
|
||||||
key += "transformation=" + this.getTransformation(imageKnifeOption.transformation) + ";"
|
key += "transformation=" + this.getTransformation(imageKnifeOption.transformation) + ";"
|
||||||
}
|
}
|
||||||
|
if ((imageKnifeOption.downsampleOf !== DownsampleStrategy.NONE && imageKnifeOption.downsampleOf !== undefined)) {
|
||||||
|
key += "downsampleOf" + imageKnifeOption.downsampleOf +"width="+width+"height="+ height
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { IEngineKey } from '../key/IEngineKey'
|
||||||
import { PixelMapTransformation } from '../transform/PixelMapTransformation'
|
import { PixelMapTransformation } from '../transform/PixelMapTransformation'
|
||||||
import common from '@ohos.app.ability.common';
|
import common from '@ohos.app.ability.common';
|
||||||
import { Size } from '@kit.ArkUI'
|
import { Size } from '@kit.ArkUI'
|
||||||
|
import { DownsampleStrategy } from '../downsampling/DownsampleStartegy'
|
||||||
|
|
||||||
export interface ImageKnifeData {
|
export interface ImageKnifeData {
|
||||||
source: PixelMap | string,
|
source: PixelMap | string,
|
||||||
|
@ -103,6 +104,9 @@ export interface RequestJobRequest {
|
||||||
fileCacheFolder: string,
|
fileCacheFolder: string,
|
||||||
isAnimator?: boolean,
|
isAnimator?: boolean,
|
||||||
moduleName?:string,
|
moduleName?:string,
|
||||||
resName?: string
|
resName?: string,
|
||||||
|
targetWidth: number
|
||||||
|
targetHeight: number
|
||||||
|
downsampType: DownsampleStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import common from '@ohos.app.ability.common'
|
||||||
import { CacheStrategy, ImageKnifeData,EventImage } from './ImageKnifeData';
|
import { CacheStrategy, ImageKnifeData,EventImage } from './ImageKnifeData';
|
||||||
import { PixelMapTransformation } from '../transform/PixelMapTransformation';
|
import { PixelMapTransformation } from '../transform/PixelMapTransformation';
|
||||||
import { drawing } from '@kit.ArkGraphics2D';
|
import { drawing } from '@kit.ArkGraphics2D';
|
||||||
|
import { DownsampleStrategy } from '../downsampling/DownsampleStartegy';
|
||||||
|
|
||||||
export interface HeaderOptions {
|
export interface HeaderOptions {
|
||||||
key: string;
|
key: string;
|
||||||
|
@ -90,6 +91,7 @@ interface ImageOption {
|
||||||
onLoadListener?: OnLoadCallBack | undefined;
|
onLoadListener?: OnLoadCallBack | undefined;
|
||||||
onComplete?:(event:EventImage | undefined) => void
|
onComplete?:(event:EventImage | undefined) => void
|
||||||
drawingColorFilter?: ColorFilter | drawing.ColorFilter
|
drawingColorFilter?: ColorFilter | drawing.ColorFilter
|
||||||
|
downsampleOf?: DownsampleStrategy
|
||||||
}
|
}
|
||||||
@ObservedV2
|
@ObservedV2
|
||||||
export class ImageKnifeOption {
|
export class ImageKnifeOption {
|
||||||
|
@ -121,6 +123,8 @@ export class ImageKnifeOption {
|
||||||
onLoadListener?: OnLoadCallBack | undefined;
|
onLoadListener?: OnLoadCallBack | undefined;
|
||||||
onComplete?:(event:EventImage | undefined) => void
|
onComplete?:(event:EventImage | undefined) => void
|
||||||
drawingColorFilter?: ColorFilter | drawing.ColorFilter
|
drawingColorFilter?: ColorFilter | drawing.ColorFilter
|
||||||
|
// 下采样
|
||||||
|
@Trace downsampleOf: DownsampleStrategy = DownsampleStrategy.NONE
|
||||||
constructor(option?:ImageOption) {
|
constructor(option?:ImageOption) {
|
||||||
this.loadSrc = option?.loadSrc == undefined ? "" : option?.loadSrc
|
this.loadSrc = option?.loadSrc == undefined ? "" : option?.loadSrc
|
||||||
this.placeholderSrc = option?.placeholderSrc
|
this.placeholderSrc = option?.placeholderSrc
|
||||||
|
@ -141,6 +145,7 @@ export class ImageKnifeOption {
|
||||||
this.onLoadListener = option?.onLoadListener
|
this.onLoadListener = option?.onLoadListener
|
||||||
this.onComplete = option?.onComplete
|
this.onComplete = option?.onComplete
|
||||||
this.drawingColorFilter = option?.drawingColorFilter
|
this.drawingColorFilter = option?.drawingColorFilter
|
||||||
|
this.downsampleOf = option?.downsampleOf==undefined?DownsampleStrategy.NONE:option?.downsampleOf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
import { ImageKnifeOption } from './ImageKnifeOption';
|
import { ImageKnifeOption } from './ImageKnifeOption';
|
||||||
import common from '@ohos.app.ability.common';
|
import common from '@ohos.app.ability.common';
|
||||||
import { ImageKnifeRequestSource } from './ImageKnifeData';
|
import { ImageKnifeRequestSource } from './ImageKnifeData';
|
||||||
|
import { DownsampleStrategy } from '../downsampling/DownsampleStartegy';
|
||||||
|
|
||||||
|
|
||||||
export class ImageKnifeRequest {
|
export class ImageKnifeRequest {
|
||||||
|
@ -27,18 +28,22 @@ export class ImageKnifeRequest {
|
||||||
ImageKnifeRequestCallback: ImageKnifeRequestCallback
|
ImageKnifeRequestCallback: ImageKnifeRequestCallback
|
||||||
componentVersion: number = 0
|
componentVersion: number = 0
|
||||||
headers: Map<string,Object> = new Map<string,Object>()
|
headers: Map<string,Object> = new Map<string,Object>()
|
||||||
|
downsampType?: DownsampleStrategy
|
||||||
constructor(option: ImageKnifeOption,
|
constructor(option: ImageKnifeOption,
|
||||||
uIAbilityContext: common.UIAbilityContext,
|
uIAbilityContext: common.UIAbilityContext,
|
||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
version: number,
|
version: number,
|
||||||
ImageKnifeRequestCallback: ImageKnifeRequestCallback) {
|
ImageKnifeRequestCallback: ImageKnifeRequestCallback,
|
||||||
|
downsampType?: DownsampleStrategy
|
||||||
|
) {
|
||||||
this.imageKnifeOption = option
|
this.imageKnifeOption = option
|
||||||
this.context = uIAbilityContext
|
this.context = uIAbilityContext
|
||||||
this.componentWidth = width
|
this.componentWidth = width
|
||||||
this.componentHeight = height
|
this.componentHeight = height
|
||||||
this.componentVersion = version
|
this.componentVersion = version
|
||||||
this.ImageKnifeRequestCallback = ImageKnifeRequestCallback
|
this.ImageKnifeRequestCallback = ImageKnifeRequestCallback
|
||||||
|
this.downsampType = downsampType
|
||||||
}
|
}
|
||||||
// RequestOption调用header对于的方法
|
// RequestOption调用header对于的方法
|
||||||
addHeader(key: string, value: Object) {
|
addHeader(key: string, value: Object) {
|
||||||
|
|
Loading…
Reference in New Issue