/*
* Copyright (C) 2025 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 { ImageKnifeLoader } from "../ImageKnifeLoader";
import {
DecodeImageInfo,
ImageKnifeData,
ImageKnifeRequestSource,
RequestJobRequest,
RequestJobResult,
TimeInfo
} from "../model/ImageKnifeData";
import { LoadPhase, LoadPixelMapCode } from "../utils/Constants";
import { IParseImage } from "./IParseImage";
import { image, sendableImage } from "@kit.ImageKit";
import { BusinessError, deviceInfo } from "@kit.BasicServicesKit";
import { LogUtil } from "../utils/LogUtil";
import { DownsampleStrategy } from "../downsampling/DownsampleStartegy";
import { hiTraceMeter } from "@kit.PerformanceAnalysisKit";
export class ParseStaticImage implements IParseImage {
async parseImage(
resBuf: ArrayBuffer,
typeValue: string,
fileKey: string,
request: RequestJobRequest,
callBackData: ImageKnifeData
): Promise<RequestJobResult | undefined> {
let resPixelmap: PixelMap | undefined = undefined
hiTraceMeter.startTrace('parseImage', request.componentId)
let timeInfo: TimeInfo = ImageKnifeLoader.getTimeInfo(callBackData);
let pixel: sendableImage.PixelMap | undefined = undefined
let rangeMode: image.DecodingDynamicRange | undefined = undefined
switch (request.dynamicRangeMode) {
case DynamicRangeMode.HIGH:
rangeMode = image.DecodingDynamicRange.HDR;
break;
case DynamicRangeMode.CONSTRAINT:
rangeMode = image.DecodingDynamicRange.AUTO;
break;
case DynamicRangeMode.STANDARD:
rangeMode = image.DecodingDynamicRange.SDR;
break;
}
let decodingOptions: image.DecodingOptions = {
editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true :
false,
desiredDynamicRange: rangeMode
}
let imageSource: image.ImageSource = image.createImageSource(resBuf)
if (imageSource === undefined) {
ImageKnifeLoader.makeEmptyResult(request, 'image.createImageSource failed',
ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_CREATE_SOURCE,
LoadPixelMapCode.IMAGE_SOURCE_ERROR_CODE))
return
}
let imageInfoSync = imageSource.getImageInfoSync()
if (imageInfoSync === undefined) {
imageSource.release()
ImageKnifeLoader.makeEmptyResult(request, 'getImageInfoSync failed')
return
}
let size = imageInfoSync.size
callBackData.imageWidth = size.width;
callBackData.imageHeight = size.height;
if (request.isAutoImageFit && request.requestSource === ImageKnifeRequestSource.SRC) {
request.componentHeight = request.componentWidth * size.height / size.width
}
try {
if ((request.downsampType !== DownsampleStrategy.NONE) &&
request.requestSource === ImageKnifeRequestSource.SRC) {
decodingOptions.desiredSize =
ImageKnifeLoader.getDownsamplerDecodingOptions(typeValue, request, size, ImageKnifeRequestSource.SRC)
}
} catch (err) {
imageSource.release()
ImageKnifeLoader.makeEmptyResult(request, 'getDownsamplerDecodingOptions failed:' + err)
return
}
// 如果开启jpeg解码优化、类型是jpeg/jpg且没有图形变换后处理(配置transformation),设置YUV格式解码
if (request.jpegOptimizeDecoding && !request.transformation && typeValue === 'jpg') {
decodingOptions.desiredPixelFormat = image.PixelMapFormat.NV12;
LogUtil.log(`decodingOptions.desiredPixelFormat is image.PixelMapFormat.NV12 : ${ request.componentId },srcType:${request.requestSource}, ${request.componentVersion}`)
}
timeInfo.decodeStartTime = Date.now();
// 获取旋转信息
let exif: string | undefined = undefined;
await imageSource.getImageProperty(image.PropertyKey.ORIENTATION).then((res) => {
exif = res;
}).catch((error: BusinessError) => {
LogUtil.info("The normal image don't have rotation information, " + error.message);
})
await imageSource.createPixelMap(decodingOptions)
.then((pixelmap: PixelMap) => {
timeInfo.decodeEndTime = Date.now();
resPixelmap = pixelmap
imageSource.release()
}).catch((error: BusinessError) => {
timeInfo.decodeEndTime = Date.now();
imageSource.release()
ImageKnifeLoader.makeEmptyResult(request, 'createPixelMap failed:' + JSON.stringify(error),
ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_CREATE_PIXEL_MAP,
LoadPixelMapCode.IMAGE_DECODE_ERROR_CODE))
return
})
if (request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined &&
resPixelmap !== undefined) {
LogUtil.log('requestJob.transform.start:' + request.componentId + ',srcType:' + request.requestSource + ',' +
request.componentVersion)
resPixelmap = await request.transformation?.transform(request.context, resPixelmap, request.componentWidth,
request.componentHeight);
LogUtil.log('requestJob.transform.end:' + request.componentId + ',srcType:' + request.requestSource + ',' +
request.componentVersion)
}
try {
resPixelmap?.setTransferDetached(true)
} catch (e) {
LogUtil.error('PixelMap setTransferDetached failed:' + JSON.stringify(e))
}
// 设置翻转和旋转角度
if (exif && exif !== 'Top-left') {
let result = ImageKnifeLoader.getOrientation(exif);
if (result.horizontal || result.vertical) {
resPixelmap?.flipSync(result.horizontal, result.vertical);
}
if (result.rotate > 0) {
resPixelmap?.rotateSync(result.rotate);
}
LogUtil.log('The normal image set flip , horizontal=' + result.horizontal + ', vertical=' + result.vertical +
', rotate=' + result.rotate);
}
if (request.pixelName !== undefined) {
if (deviceInfo.sdkApiVersion < 13) {
LogUtil.error('api not support setPixelName')
} else {
resPixelmap?.setMemoryNameSync(request.pixelName)
}
}
//获取各个pixelMap的大小
if (resPixelmap !== undefined) {
let decodeImages: DecodeImageInfo[] = [];
let size = (resPixelmap as PixelMap).getImageInfoSync().size;
let decodeImage: DecodeImageInfo = {
contentWidth: size.width,
contentHeight: size.height,
contentSize: (resPixelmap as PixelMap).getPixelBytesNumber()
}
decodeImages.push(decodeImage);
callBackData.decodeImages = decodeImages;
pixel = sendableImage.convertFromPixelMap(resPixelmap)
}
LogUtil.log('image parse pixelmap end:' + request.componentId + ',srcType:' + request.requestSource + ',' +
request.componentVersion)
hiTraceMeter.finishTrace('parseImage', request.componentId)
return {
pixelMap: pixel,
bufferSize: resBuf.byteLength,
fileKey: fileKey,
size: size,
type: typeValue,
imageKnifeData: callBackData
}
}
}