!268 ImageKnife支持多种组合变换

Merge pull request !268 from zhanghanyong/master
This commit is contained in:
openharmony_ci 2024-05-22 09:02:14 +00:00 committed by Gitee
commit 3ecc586559
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
6 changed files with 533 additions and 70 deletions

View File

@ -2,6 +2,7 @@
- 修复错误图绘制完后变成占位图
- 提供图片加载成功/失败的事件
- 修复懒加载在多次点击出现卡死的问题
- 支持多种组合变换
## 2.2.0-rc.2
- ImageKnife支持下采样

View File

@ -170,7 +170,7 @@ ImageKnifeGlobal.getInstance().getImageKnife()?.replaceDataFetch(new CustomDataF
| isCacheable | boolean | 是否开启一级内存缓存(可选) |
| gif | {<br/> // 返回一周期动画gif消耗的时间<br/> loopFinish?: (loopTime?) => void<br/> // gif播放速率相关<br/> speedFactory?: number<br/> // 直接展示gif第几帧数据<br/> seekTo?: number<br/> playTimes?: number<br/> } | GIF播放控制能力可选 |
| transformation | BaseTransform<PixelMap> | 单个变换(可选) |
| transformations | Array<BaseTransform<PixelMap>> | 多个变换,目前仅支持单个变换(可选) |
| transformations | Array<BaseTransform<PixelMap>> | 多个变换(可选) |
| allCacheInfoCallback | IAllCacheInfoCallback | 输出缓存相关内容和信息(可选) |
| signature | ObjectKey | 自定key可选 |
| **drawLifeCycle** | **IDrawLifeCycle** | **用户自定义实现绘制方案(可选)** |
@ -610,9 +610,7 @@ HSP场景适配:
## 遗留问题
1.目前只支持一种图片变换效果。
2.目前svg和gif动图不支持变换效果。
1.目前svg和gif动图不支持变换效果。
## 补充说明
### SVG标签说明

View File

@ -173,6 +173,11 @@ struct IndexFunctionDemo {
console.log("测试图片变换")
router.pushUrl({ url: "pages/transformPixelMapPage" });
}).margin({ top: 5, left: 3 })
Button("图片组合变换")
.onClick(() => {
console.log("图片组合变换")
router.pushUrl({ url: "pages/transformsPage" });
}).margin({ top: 5, left: 3 })
Button("测试图片压缩")
.onClick(() => {
console.log("测试图片压缩")

View File

@ -0,0 +1,461 @@
/*
* 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 {
GrayscaleTransformation,
ImageKnifeComponent,
ImageKnifeOption,
RotateImageTransformation,
SketchFilterTransformation,
CropCircleTransformation,
ToonFilterTransform,
VignetteFilterTransform,
BaseTransform,
BlurTransformation,
BrightnessFilterTransformation,
InvertFilterTransformation,
CropCircleWithBorderTransformation,
CropSquareTransformation,
CropTransformation,
CropType,
ContrastFilterTransformation,
PixelationFilterTransformation,
RoundedCornersTransformation,
SepiaFilterTransformation,
MaskTransformation,
SwirlFilterTransformation,
KuwaharaFilterTransform
} from '@ohos/libraryimageknife'
@Entry
@Component
struct transformsPage {
@State imageKnifeOption: ImageKnifeOption =
{
loadSrc: $r('app.media.jpgSample'),
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
};
private transformations: Array<BaseTransform<PixelMap>> = new Array();
private blurTransformation: BlurTransformation = new BlurTransformation(15, 3);
private brightnessFilterTransformation: BrightnessFilterTransformation = new BrightnessFilterTransformation(0.5);
private contrastFilterTransformation: ContrastFilterTransformation = new ContrastFilterTransformation(2.0);
private cropCircleTransformation: CropCircleTransformation = new CropCircleTransformation();
private cropCircleWithBorderTransformation: CropCircleWithBorderTransformation =
new CropCircleWithBorderTransformation(16, { r_color: 100, g_color: 100, b_color: 100 });
private cropSquareTransformation: CropSquareTransformation = new CropSquareTransformation();
private cropTransformation: CropTransformation = new CropTransformation(100, 100, CropType.CENTER);
private grayscaleTransformation: GrayscaleTransformation = new GrayscaleTransformation();
private invertFilterTransformation: InvertFilterTransformation = new InvertFilterTransformation();
private pixelationFilterTransformation: PixelationFilterTransformation = new PixelationFilterTransformation(5.0);
private rotateImageTransformation: RotateImageTransformation = new RotateImageTransformation(90);
private roundedCornersTransformation: RoundedCornersTransformation = new RoundedCornersTransformation({
top_left: 5,
bottom_left: 5,
top_right: 5,
bottom_right: 5
});
private sepiaFilterTransformation: SepiaFilterTransformation = new SepiaFilterTransformation();
private sketchFilterTransformation: SketchFilterTransformation = new SketchFilterTransformation();
private maskTransformation: MaskTransformation = new MaskTransformation($r('app.media.mask_starfish'));
private swirlFilterTransformation: SwirlFilterTransformation = new SwirlFilterTransformation(80);
private kuwaharaFilterTransform: KuwaharaFilterTransform = new KuwaharaFilterTransform(10);
private toonFilterTransform: ToonFilterTransform = new ToonFilterTransform(0.3, 10.0);
private vignetteFilterTransform: VignetteFilterTransform = new VignetteFilterTransform([0.5, 0.5], [0.0, 0.0, 0.0], [0.3, 0.75]);
build() {
Scroll() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Flex({ direction: FlexDirection.Row }) {
Row() {
Checkbox({ name: '模糊' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.blurTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.blurTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('模糊变换').margin({ left: 16 })
}.margin({ left: 16 })
Row() {
Checkbox({ name: '亮度滤波' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.brightnessFilterTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.brightnessFilterTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('亮度滤波').margin({ left: 16 })
}.margin({ left: 16 })
}.margin({ top: 32, left: 16 })
Flex({ direction: FlexDirection.Row }) {
Row() {
Checkbox({ name: '对比度' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.contrastFilterTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.contrastFilterTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('对比度').margin({ left: 16 })
}.margin({ left: 16 })
Row() {
Checkbox({ name: '圆形裁剪' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.cropCircleTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.cropCircleTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('圆形裁剪').margin({ left: 16 })
}.margin({ left: 16 })
}.margin({ top: 32, left: 16 })
Flex({ direction: FlexDirection.Row }) {
Row() {
Checkbox({ name: '圆环' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.cropCircleWithBorderTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.cropCircleWithBorderTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('圆环').margin({ left: 16 })
}.margin({ left: 16 })
Row() {
Checkbox({ name: '正方形裁剪' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.cropSquareTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.cropSquareTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('正方形裁剪').margin({ left: 16 })
}.margin({ left: 16 })
}.margin({ top: 32, left: 16 })
Flex({ direction: FlexDirection.Row }) {
Row() {
Checkbox({ name: '自定义矩形' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.cropTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.cropTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('自定义矩形').margin({ left: 16 })
}.margin({ left: 16 })
Row() {
Checkbox({ name: '灰度' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.grayscaleTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.grayscaleTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('灰度').margin({ left: 16 })
}.margin({ left: 16 })
}.margin({ top: 32, left: 16 })
Flex({ direction: FlexDirection.Row }) {
Row() {
Checkbox({ name: '反转' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.invertFilterTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.invertFilterTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('反转').margin({ left: 16 })
}.margin({ left: 16 })
Row() {
Checkbox({ name: '像素化' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.pixelationFilterTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.pixelationFilterTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('像素化').margin({ left: 16 })
}.margin({ left: 16 })
}.margin({ top: 32, left: 16 })
Flex({ direction: FlexDirection.Row }) {
Row() {
Checkbox({ name: '旋转' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.rotateImageTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.rotateImageTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('旋转').margin({ left: 16 })
}.margin({ left: 16 })
Row() {
Checkbox({ name: '圆角' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.roundedCornersTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.roundedCornersTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('圆角').margin({ left: 16 })
}.margin({ left: 16 })
}.margin({ top: 32, left: 16 })
Flex({ direction: FlexDirection.Row }) {
Row() {
Checkbox({ name: '乌墨色滤波' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.sepiaFilterTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.sepiaFilterTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('乌墨色滤波').margin({ left: 16 })
}.margin({ left: 16 })
Row() {
Checkbox({ name: '素描' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.sketchFilterTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.sketchFilterTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('素描').margin({ left: 16 })
}.margin({ left: 16 })
}.margin({ top: 32, left: 16 })
Flex({ direction: FlexDirection.Row }) {
Row() {
Checkbox({ name: '遮罩' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.maskTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.maskTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('遮罩').margin({ left: 16 })
}.margin({ left: 16 })
Row() {
Checkbox({ name: '扭曲滤波' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.swirlFilterTransformation);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.swirlFilterTransformation.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('扭曲滤波').margin({ left: 16 })
}.margin({ left: 16 })
}.margin({ top: 32, left: 16 })
Flex({ direction: FlexDirection.Row }) {
Row() {
Checkbox({ name: '桑原滤波' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.kuwaharaFilterTransform);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.kuwaharaFilterTransform.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('桑原滤波').margin({ left: 16 })
}.margin({ left: 16 })
Row() {
Checkbox({ name: '动画滤波' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.toonFilterTransform);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.toonFilterTransform.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('动画滤波').margin({ left: 16 })
}.margin({ left: 16 })
}.margin({ top: 32, left: 16 })
Flex({ direction: FlexDirection.Row }) {
Row() {
Checkbox({ name: '装饰滤波' })
.width(32)
.height(32)
.onChange((value: boolean) => {
if (value) {
this.transformations.push(this.vignetteFilterTransform);
} else {
for (let index = 0; index < this.transformations.length; index++) {
if (this.transformations[index].getClassName() == this.vignetteFilterTransform.getClassName()) {
this.transformations.splice(index, 1);
}
}
}
})
Text('装饰滤波').margin({ left: 16 })
}.margin({ left: 16 })
}.margin({ top: 32, left: 16 })
Button("显示效果").onClick(() => {
this.imageKnifeOption = {
loadSrc: $r('app.media.jpgSample'),
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed'),
enableGpu: true,
transformations: this.transformations
};
}).margin({ left: 5 }).backgroundColor(Color.Blue)
Text("下面为展示图片区域").margin({ top: 5 })
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption }).width(300).height(300)
}.width(400).height(400).margin({ top: 10 }).backgroundColor(Color.Pink)
}
}
.width('100%')
.height('100%')
}
}

View File

@ -11,6 +11,7 @@
"pages/pngjTestCasePage",
"pages/showErrorholderTestCasePage",
"pages/transformPixelMapPage",
"pages/transformsPage",
"pages/testPreloadPage",
"pages/testDiskPreLoadPage",
"pages/testImageKnifeOptionChangedPage",

View File

@ -33,7 +33,9 @@ import { GIFFrame } from '../utils/gif/GIFFrame'
import { LogUtil } from '../../imageknife/utils/LogUtil'
import { BusinessError } from '@ohos.base'
import { DataFetchResult } from '../networkmanage/DataFetchResult'
import { BaseTransform } from '../transform/BaseTransform'
const imagePackerApi = image.createImagePacker();
export enum Stage {
@ -173,16 +175,7 @@ export class RequestManager {
this.svgProcess(request, onComplete, onError, arrayBuffer, typeValue)
} else {
if (request.transformations[0]) {
request.transformations[0].transform(arrayBuffer, request, {
asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => {
// 输出给Image
if (pixelMap) {
onComplete(pixelMap);
} else {
onError(error);
}
}
})
this.getTransformImage(request, arrayBuffer, request.transformations, 0, onComplete, onError);
}
else {
let success = (value: PixelMap) => {
@ -201,6 +194,60 @@ export class RequestManager {
}
private getTransformImage(request: RequestOption, source: ArrayBuffer, transformations: Array<BaseTransform<PixelMap>>, index: number,
onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike<ImageKnifeData>, onError: (reason?: BusinessError | string) => void) {
transformations[index].transform(source, request, {
asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => {
// 输出给Image
if (pixelMap) {
if (index == request.transformations.length - 1) {
onComplete(pixelMap)
} else {
let packOpts: image.PackingOption = { format: "image/png", quality: 98 };
imagePackerApi.packing(pixelMap, packOpts).then((data: ArrayBuffer) => {
// data 为打包获取到的文件流,写入文件保存即可得到一张图片
index++;
this.getTransformImage(request, data, transformations, index, onComplete, onError);
}).catch((error: BusinessError) => {
onError(error);
console.error('requestManager Failed to pack the image. And the error is: ' + JSON.stringify(error));
})
}
} else {
onError(error);
console.error('requestManager getTransformImage error! ' + JSON.stringify(error));
}
}
})
}
private saveTransformImage(request: RequestOption, source: ArrayBuffer, transformations: Array<BaseTransform<PixelMap>>, index: number, filetype: string,
onComplete: (value: PixelMap) => void | PromiseLike<ImageKnifeData>, onError: (reason?: BusinessError | string) => void) {
transformations[index].transform(source, request, {
asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => {
// 输出给Image
if (pixelMap) {
if (index == request.transformations.length - 1) {
this.saveCacheAndDisk(pixelMap, filetype, onComplete, source);
} else {
let packOpts: image.PackingOption = { format: "image/png", quality: 98 };
imagePackerApi.packing(pixelMap, packOpts).then((data: ArrayBuffer) => {
// data 为打包获取到的文件流,写入文件保存即可得到一张图片
index++;
this.saveTransformImage(request, data, transformations, index, filetype, onComplete, onError);
}).catch((error: BusinessError) => {
onError(error);
console.error('requestManager Failed to pack the image. And the error is: ' + JSON.stringify(error));
})
}
} else {
onError(error);
console.error('requestManager saveTransformImage error! ' + JSON.stringify(error));
}
}
})
}
// 加载磁盘缓存 原图
private loadDiskFromSource(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike<ImageKnifeData>, onError: (reason?: BusinessError | string) => void) {
LogUtil.log("ImageKnife RequestManager loadDiskFromSource")
@ -286,37 +333,13 @@ export class RequestManager {
}
let thumbCallback = this.options.thumbholderOnComplete;
let thumbError = this.options.thumbholderOnError;
this.options.transformations[0].transform(source, thumbOption, {
asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => {
if (pixelMap) {
thumbCallback(pixelMap);
} else {
thumbError(error);
}
}
})
this.getTransformImage(thumbOption, source, this.options.transformations, 0, thumbCallback, thumbError);
setTimeout(() => {
this.options.transformations[0].transform(source, request, {
asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => {
if (pixelMap) {
onComplete(pixelMap);
} else {
onError(error);
}
}
})
this.getTransformImage(request, source, this.options.transformations, 0, onComplete, onError);
}, this.options.thumbDelayTime);
}
else {
this.options.transformations[0].transform(source, request, {
asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => {
if (pixelMap) {
onComplete(pixelMap);
} else {
onError(error);
}
}
})
this.getTransformImage(request, source, this.options.transformations, 0, onComplete, onError);
}
} else {
// thumbnail 缩略图部分
@ -427,17 +450,7 @@ export class RequestManager {
this.thumbnailProcess(source, filetype, onComplete, onError);
}
} else {
this.options.transformations[0].transform(source, this.options, {
asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => {
if (pixelMap) {
if (filetype != null) {
this.saveCacheAndDisk(pixelMap, filetype, onComplete, source);
}
} else {
onError(error);
}
}
})
this.saveTransformImage(this.options, source, this.options.transformations, 0, filetype, onComplete, onError);
}
} else {
// thumbnail 缩略图部分
@ -507,25 +520,9 @@ export class RequestManager {
})
let thumbCallback = this.options.thumbholderOnComplete
let thumbError = this.options.thumbholderOnError
this.options.transformations[0].transform(source, thumbOption, {
asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => {
if (pixelMap) {
thumbCallback(pixelMap);
} else {
thumbError(error);
}
}
})
this.getTransformImage(thumbOption, source, this.options.transformations, 0, thumbCallback, thumbError);
setTimeout(() => {
this.options.transformations[0].transform(source, this.options, {
asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => {
if (pixelMap) {
this.saveCacheAndDisk(pixelMap, filetype, onComplete, source);
} else {
onError(error);
}
}
})
this.saveTransformImage(this.options, source, this.options.transformations, 0, filetype, onComplete, onError);
}, this.options.thumbDelayTime)
}