优化ImageKnifeComponent的ImageKnifeOption耗时

Signed-off-by: 张欢 <zhanghuan165@h-partners.com>
This commit is contained in:
张欢 2024-04-20 17:57:05 +08:00
parent 83075cc204
commit 77949e2b91
10 changed files with 1351 additions and 71 deletions

View File

@ -9,6 +9,7 @@
- 修改全局请求头覆盖request请求头
- imageKnife支持heic测试demo独立页面展示
- drawLifeCycle支持gif图
- 优化ImageKnifeComponent的ImageKnifeOption耗时
## 2.1.2-rc.12
- 新增gif播放次数功能

View File

@ -0,0 +1,94 @@
/*
* 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 {
ImageKnifeComponent,
ImageKnifeOption,
HeaderOptions,
ImageKnifeOptionNext
} from '@ohos/libraryimageknife'
@Entry
@Component
struct IndexPerformance {
@State headerOptions1: HeaderOptions = {
key: "refer",
value: "http://1.94.37.200:7070/AntiTheftChain/downloadImage"
};
@State imageKnifeOption1: ImageKnifeOptionNext =
{
loadSrc: $r('app.media.icon'),
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed'),
headerOption: [this.headerOptions1]
};
@State imageKnifeOption2: ImageKnifeOptionNext =
{
loadSrc: $r('app.media.icon'),
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed'),
headerOption: [this.headerOptions1]
};
@State imageKnifeOption3: ImageKnifeOption =
{
loadSrc: $r('app.media.yunHeic'),
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed'),
};
@State flag: boolean = true;
build() {
Scroll() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text("简单示例1加载一张本地png图片").fontSize(15)
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button("加载PNG")
.onClick(() => {
this.imageKnifeOption1.loadSrc = $r('app.media.pngSample')
}).margin({ top: 5, left: 3 })
ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).width(300).height(300)
}.width('100%').backgroundColor(Color.Pink)
Text("简单示例2加载一张网络gif图片").fontSize(15)
Text("gif解析在子线程,请在页面构建后创建worker,注入imageknife").fontSize(15)
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button("加载GIF")
.onClick(() => {
this.imageKnifeOption2.loadSrc = 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658'
}).margin({ top: 5, left: 3 })
ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption2 }).width(300).height(300)
}.width('100%').backgroundColor(Color.Pink)
}
}
.width('100%')
.height('100%')
}
aboutToAppear() {
}
aboutToDisappear() {
}
onBackPress() {
}
}

View File

@ -396,6 +396,13 @@ struct IndexFunctionDemo {
router.pushUrl({ url: 'pages/testImageKnifeHeic' });
}).margin({ top: 5, left: 3 })
}.width('100%').height(60).backgroundColor(Color.Pink)
Text('测试ImageKnifeOptionNext').fontSize(15)
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button('测试ImageKnifeOptionNext')
.onClick(() => {
router.pushUrl({ url: 'pages/IndexPerformance' });
}).margin({ top: 5, left: 3 })
}.width('100%').height(60).backgroundColor(Color.Pink)
}
}
.width('100%')

View File

@ -53,6 +53,7 @@
"pages/webpImageTestPage",
"pages/testStopPlayingGifPage",
"pages/testImageKnifeDataFetch",
"pages/testImageKnifeHeic"
"pages/testImageKnifeHeic",
"pages/IndexPerformance"
]
}

View File

@ -110,6 +110,8 @@ export {IDataFetch} from './src/main/ets/components/imageknife/networkmanage/IDa
export {ICache} from './src/main/ets/components/imageknife/requestmanage/ICache'
export { FileTypeUtil } from './src/main/ets/components/imageknife/utils/FileTypeUtil'
export { ParseImageUtil } from './src/main/ets/components/imageknife/utils/ParseImageUtil'
export { ImageKnifeComponentNext } from './src/main/ets/components/imageknife/ImageKnifeComponentNext'
export { ImageKnifeOptionNext } from './src/main/ets/components/imageknife/ImageKnifeOptionNext'
/**
* svg parse

View File

@ -27,7 +27,6 @@ import { ObjectKey } from './ObjectKey'
import componentUtils from '@ohos.arkui.componentUtils'
import inspector from '@ohos.arkui.inspector'
import util from '@ohos.util'
import { ImageKnifeDrawFactory } from './ImageKnifeDrawFactory'
interface KeyCanvas {
keyId:string
@ -35,13 +34,14 @@ interface KeyCanvas {
@Component
export struct ImageKnifeComponent {
@Watch('watchImageKnifeOption') @ObjectLink imageKnifeOption: ImageKnifeOption;
autoPlay: boolean = true
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private hasDisplayRetryholder = false;
private lastWidth: number = 0
private lastHeight: number = 0
// 当前帧数位置
private renderFrames_index = 0;
@State currentWidth : number | string = "100%"
@State currentHeight : number | string = "100%"
// 定时器id
private gifTimerId: number = -1
// 完整gif播放时间
@ -108,16 +108,12 @@ export struct ImageKnifeComponent {
private lastSrc: string | Resource | PixelMap = ""
listener: inspector.ComponentObserver = inspector.createComponentObserver(this.keyCanvas.keyId)
@State currentSize : Size = {
width: 0.01,
height: 0.01
}
build() {
Canvas(this.context)
.key(this.keyCanvas.keyId)
.width((this.imageKnifeOption!=undefined && this.imageKnifeOption.mainScaleType!= undefined && this.imageKnifeOption.mainScaleType == ScaleType.AUTO_WIDTH )? this.currentSize.width:'100%')
.height((this.imageKnifeOption!=undefined && this.imageKnifeOption.mainScaleType!= undefined && this.imageKnifeOption.mainScaleType == ScaleType.AUTO_HEIGHT )? this.currentSize.height:'100%')
.width(this.currentWidth)
.height(this.currentHeight)
.renderFit(RenderFit.RESIZE_FILL)
.onReady(() => {
let ctx = this.context;
@ -416,7 +412,6 @@ export struct ImageKnifeComponent {
}
displayMainSource = (data: ImageKnifeData|number|undefined)=> {
ImageKnifeDrawFactory.type = undefined;
if(data == undefined || typeof data == 'number'){
return
}
@ -598,12 +593,12 @@ export struct ImageKnifeComponent {
let scaleHeight = imageInfo.size.height/imageInfo.size.width
let scaleWidth = imageInfo.size.width/imageInfo.size.height
if (this.imageKnifeOption.mainScaleType == ScaleType.AUTO_WIDTH){
this.currentSize.width=this.context.height*scaleWidth
this.currentWidth=this.context.height*scaleWidth
}else if (this.imageKnifeOption.mainScaleType == ScaleType.AUTO_HEIGHT){
this.currentSize.height=this.context.width*scaleHeight
this.currentHeight=this.context.width*scaleHeight
}else if (this.imageKnifeOption.mainScaleType == ScaleType.AUTO){
this.currentSize.height=imageInfo.size.height
this.currentSize.width =imageInfo.size.width
this.currentHeight=imageInfo.size.height
this.currentWidth =imageInfo.size.width
}
ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap?.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight, 0, 0)
context.restore();
@ -767,15 +762,15 @@ export struct ImageKnifeComponent {
let frames = data.drawGIFFrame?.imageGIFFrames as GIFFrame[]
LogUtil.log('ImageKnifeComponent gifFrameLength =' + frames.length);
if (imageKnifeOption.gif && (typeof (imageKnifeOption.gif.seekTo) == 'number') && imageKnifeOption.gif.seekTo >= 0) {
this.autoPlay = false;
context.clearRect(0, 0, compWidth, compHeight)
this.drawSeekToFrame(frames, context, compWidth, compHeight)
} else {
this.autoPlay = true
context.clearRect(0, 0, compWidth, compHeight)
this.renderFrames_frames = frames
if (this.imageKnifeOption.autoPlay == undefined) {
this.renderFrames_index = 0;
}
this.renderFrames_index = 0
this.renderFrames_context = context
this.renderFrames_compWidth = compWidth
this.renderFrames_compHeight = compHeight
@ -799,13 +794,13 @@ export struct ImageKnifeComponent {
private drawSeekToFrame(frames: GIFFrame[], context: CanvasRenderingContext2D, compWidth: number, compHeight: number) {
if(this.imageKnifeOption.gif != undefined && this.imageKnifeOption.gif.seekTo != undefined) {
for (let i = 0; i < this.imageKnifeOption.gif.seekTo; i++) {
this.renderFrames_index = i;
this.drawFrame(frames, i, context, compWidth, compHeight);
}
}
}
renderFrames_frames: GIFFrame[] | undefined = undefined
renderFrames_index: number = 0;
renderFrames_context: CanvasRenderingContext2D | undefined = undefined;
renderFrames_compWidth: number = 0;
renderFrames_compHeight: number = 0
@ -823,7 +818,7 @@ export struct ImageKnifeComponent {
if (this.renderFrames_frames != undefined && this.renderFrames_index === (this.renderFrames_frames.length - 1) && this.imageKnifeOption.gif != undefined ) {
this.playTimes++
if (this.imageKnifeOption.gif.playTimes == this.playTimes){
this.imageKnifeOption.autoPlay = false;
this.autoPlay = false
this.playTimes = 0
}
}
@ -836,7 +831,7 @@ export struct ImageKnifeComponent {
let end = new Date().getTime();
let diff = end - start
if ((this.imageKnifeOption.autoPlay == undefined) || (this.imageKnifeOption.autoPlay)) {
if (this.autoPlay) {
// 理论上该帧在屏幕上保留的时间
let stayTime:number= 0
@ -901,58 +896,10 @@ export struct ImageKnifeComponent {
ScaleTypeHelper.drawImageWithScaleType(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth, compHeight, px2vp(frame.dims.left), px2vp(frame.dims.top))
// tips:worker如果不是在展示页面中创建,使用子线程回来的数据创建的图片,会导致canvas绘制不出来
context.restore();
if (ImageKnifeDrawFactory.type === undefined) {
return;
}
// 通过 destination-in 裁剪出圆角
if (ImageKnifeDrawFactory?.type === DrawType.Round || ImageKnifeDrawFactory?.type === DrawType.Oval) {
context.save();
context.globalCompositeOperation = 'destination-in';
if (ImageKnifeDrawFactory.type === DrawType.Round) {
ImageKnifeDrawFactory.setRect(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth,
compHeight, 0, 0, ImageKnifeDrawFactory.borderWidth, ImageKnifeDrawFactory.connerRadius);
} else {
context.beginPath();
ImageKnifeDrawFactory.setOval(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth,
compHeight, 0, 0, ImageKnifeDrawFactory.borderWidth);
context.closePath();
}
context.fill();
context.restore();
if (ImageKnifeDrawFactory.borderWidth > 0) {
// 为圆角添加边框
context.save();
context.strokeStyle = ImageKnifeDrawFactory.colorString;
context.lineWidth = ImageKnifeDrawFactory.borderWidth;
context.globalCompositeOperation = 'source-over';
if (ImageKnifeDrawFactory.type === DrawType.Round) {
ImageKnifeDrawFactory.setRect(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth,
compHeight, 0, 0, ImageKnifeDrawFactory.borderWidth, ImageKnifeDrawFactory.connerRadius);
} else {
context.beginPath();
ImageKnifeDrawFactory.setOval(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth,
compHeight, 0, 0, ImageKnifeDrawFactory.borderWidth);
context.closePath();
}
context.stroke();
context.restore();
}
context.restore();
}
LogUtil.log('ImageKnifeComponent canvasDrawPixelMap end!')
}
}
export enum DrawType{
Normal = 0,
Oval = 1,
Round = 2
}
export enum FrameDisposalType {
// 0 - No disposal specified. The decoder is not required to take any action.
// 不使用处置方法

File diff suppressed because it is too large Load Diff

View File

@ -69,8 +69,6 @@ export interface HeaderOptions {
@Observed
export class ImageKnifeOption {
//控制gif开关
autoPlay?: boolean = true;
// header请求列表
headerOption?: Array<HeaderOptions>;
// 主图资源

View File

@ -0,0 +1,148 @@
/*
* Copyright (C) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AUTOMATIC } from '../cache/diskstrategy/enum/AUTOMATIC'
import { DiskStrategy } from '../cache/diskstrategy/DiskStrategy'
import { BaseTransform } from '../imageknife/transform/BaseTransform'
import { TransformType } from '../imageknife/transform/TransformType'
import { CropType } from '../imageknife/transform/CropTransformation'
import { AllCacheInfo, IAllCacheInfoCallback } from '../imageknife/interface/IAllCacheInfoCallback'
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
import { ScaleType } from '../imageknife/ImageKnifeComponent'
import { rgbColor } from './transform/CropCircleWithBorderTransformation'
import { RoundCorner } from './transform/RoundedCornersTransformation'
import { ObjectKey } from './ObjectKey'
import common from '@ohos.app.ability.common'
import { Priority } from './RequestOption'
export interface CropCircleWithBorder{
border: number,
obj: rgbColor
}
export interface Crop{
width: number,
height: number,
cropType: CropType
}
export interface BlurType {
radius: number,
sampling: number
}
export interface GifOptions{
loopFinish?: (loopTime?:number) => void
speedFactory?: number
seekTo?: number
playTimes?: number
}
export interface TransformOptions{
transformType: number,
blur?: BlurType,
roundedCorners?: RoundCorner
cropCircleWithBorder?: CropCircleWithBorder
crop?:Crop
brightnessFilter?: number,
contrastFilter?: number,
pixelationFilter?: number,
swirlFilter?: number,
mask?: Resource,
rotateImage?: number
}
export interface HeaderOptions {
key: string;
value: string;
}
@Observed
export class ImageKnifeOptionNext {
// header请求列表
headerOption?: Array<HeaderOptions>;
// 主图资源
@Track
loadSrc: string | PixelMap | Resource = '';
mainScaleType?: ScaleType = ScaleType.FIT_CENTER
// 优先级
priority?: Priority = Priority.MEDIUM
enableGpu?:boolean = true;
// 磁盘缓存策略
strategy?: DiskStrategy = new AUTOMATIC();
// gif加载展示一帧
dontAnimateFlag? = false;
// 占位图
placeholderSrc?: PixelMap | Resource;
placeholderScaleType?: ScaleType = ScaleType.FIT_CENTER
// 失败占位图
errorholderSrc?: PixelMap | Resource;
errorholderSrcScaleType?: ScaleType = ScaleType.FIT_CENTER
// 重试占位图
retryholderSrc?: Resource;
retryholderScaleType?: ScaleType = ScaleType.FIT_CENTER
// 缩略图,范围(0,1)
thumbSizeMultiplier?: number;
// 缩略图展示时间
thumbSizeDelay?:number;
// 缩略图展示类型
thumbSizeMultiplierScaleType?: ScaleType = ScaleType.FIT_CENTER
// 进度条
displayProgress?: boolean;
// 自定义缓存关键字
signature?: ObjectKey;
// 重试图层 可点击
canRetryClick?: boolean;
// 仅使用缓存加载数据
onlyRetrieveFromCache?: boolean = false;
// 是否开启一级内存缓存
isCacheable?: boolean = true;
// 用户自定义实现 绘制方案
drawLifeCycle?: IDrawLifeCycle;
// 设置点击事件回调
onClick?:(event?: ClickEvent) => void
gif?: GifOptions = undefined;
// 变换相关 不推荐使用该接口 建议直接使用transformation transformations这2个接口实现
transform?:TransformOptions = undefined
transformation?: BaseTransform<PixelMap>;
transformations?: Array<BaseTransform<PixelMap>>;
// 输出缓存相关内容和信息
allCacheInfoCallback?: IAllCacheInfoCallback;
context?: common.UIAbilityContext;
// sizeAnimate?: AnimateParam 由业务自定义不再支持
constructor() {
}
}

View File

@ -110,6 +110,8 @@ export {IDataFetch} from '@ohos/imageknife'
export {ICache} from '@ohos/imageknife'
export { FileTypeUtil } from '@ohos/imageknife'
export { ParseImageUtil } from '@ohos/imageknife'
export { ImageKnifeComponentNext} from '@ohos/imageknife'
export { ImageKnifeOptionNext} from '@ohos/imageknife'
/**
* svg parse