支持图片携带的EXIF元数据作为显示方向

Signed-off-by: yang <yangweiping7@h-partners.com>
This commit is contained in:
yang 2025-02-19 10:32:55 +08:00
parent acb45a66bc
commit 115118e238
13 changed files with 224 additions and 2 deletions

View File

@ -1,3 +1,6 @@
## 3.2.2-rc.1
- Support EXIF metadata carried by images as display orientation
## 3.2.2-rc.0 ## 3.2.2-rc.0
- Add ImageKnifeComponent to destroy network request interruption - Add ImageKnifeComponent to destroy network request interruption
- Code refactoring during the download of image resources stage - Code refactoring during the download of image resources stage

View File

@ -224,6 +224,11 @@ struct Index {
}); });
}) })
Button($r('app.string.test_exif')).margin({ top: 10 }).onClick(() => {
router.push({
uri: 'pages/TestImageExif',
});
})
} }
} .width('100%') } .width('100%')
.height('100%') .height('100%')

View File

@ -0,0 +1,92 @@
/*
* 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 { ImageKnifeComponent } from '@ohos/libraryimageknife';
import fs from '@ohos.file.fs';
@Entry
@Component
struct LocalImage {
scroller: Scroller = new Scroller;
build() {
Scroll(this.scroller) {
Column() {
Column() {
Text($r('app.string.base_image'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
Row() {
Image($rawfile('rotate/rotate.jpg')).width(100).height(100).margin({ right: 10 })
ImageKnifeComponent({
imageKnifeOption: {
loadSrc: $rawfile('rotate/rotate.jpg'),
objectFit: ImageFit.Contain
}
}).width(100).height(100)
}
}
.margin({ bottom: 20 })
Column() {
Text($r('app.string.rotate_mirror'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
Row() {
Image($rawfile('rotate/rotate_mirror.jpg')).width(100).height(100).margin({ right: 10 })
ImageKnifeComponent({
imageKnifeOption: {
loadSrc: $rawfile('rotate/rotate_mirror.jpg'),
objectFit: ImageFit.Contain
}
}).width(100).height(100)
}
}.margin({ bottom: 20 })
Column() {
Text($r('app.string.rotate_rotate90'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
Row() {
Image($rawfile('rotate/rotate_rotate90.jpg')).width(100).height(100).margin({ right: 10 })
ImageKnifeComponent({
imageKnifeOption: {
loadSrc: $rawfile('rotate/rotate_rotate90.jpg'),
objectFit: ImageFit.Contain
}
}).width(100).height(100)
}
}.margin({ bottom: 20 })
Column() {
Text($r('app.string.rotate_mirror_rotate270'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
Row() {
Image($rawfile('rotate/rotate_mirror_rotate270.jpg')).width(100).height(100).margin({ right: 10 })
ImageKnifeComponent({
imageKnifeOption: {
loadSrc: $rawfile('rotate/rotate_mirror_rotate270.jpg'),
objectFit: ImageFit.Contain
}
}).width(100).height(100)
}
}
}
.width('100%')
}
.height('100%')
}
}

View File

@ -687,6 +687,26 @@
{ {
"name": "Error_Message", "name": "Error_Message",
"value": "error message" "value": "error message"
},
{
"name": "test_exif",
"value": "Test display orientation base on the EXIF metadata "
},
{
"name": "base_image",
"value": "The image don't carry rotation information"
},
{
"name": "rotate_mirror",
"value": "Mirror horizontal"
},
{
"name": "rotate_rotate90",
"value": "Rotate 90°"
},
{
"name": "rotate_mirror_rotate270",
"value": "Mirror horizontal and rotate 270°"
} }
] ]
} }

View File

@ -42,6 +42,7 @@
"pages/SingleImageCallBack", "pages/SingleImageCallBack",
"pages/MultipleImageCallBack", "pages/MultipleImageCallBack",
"pages/LocalImage", "pages/LocalImage",
"pages/ErrorMessageDownload" "pages/ErrorMessageDownload",
"pages/TestImageExif"
] ]
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -679,6 +679,26 @@
{ {
"name": "Error_Message", "name": "Error_Message",
"value": "错误信息" "value": "错误信息"
},
{
"name": "base_image",
"value": "图片不携带旋转信息"
},
{
"name": "test_exif",
"value": "测试图片携带的EXIF元数据作为显示方向"
},
{
"name": "rotate_mirror",
"value": "水平翻转"
},
{
"name": "rotate_rotate90",
"value": "顺时针90°"
},
{
"name": "rotate_mirror_rotate270",
"value": "水平翻转后再顺时针270°"
} }
] ]
} }

View File

@ -14,7 +14,7 @@
"main": "index.ets", "main": "index.ets",
"repository": "https://gitee.com/openharmony-tpc/ImageKnife", "repository": "https://gitee.com/openharmony-tpc/ImageKnife",
"type": "module", "type": "module",
"version": "3.2.2-rc.0", "version": "3.2.2-rc.1",
"dependencies": { "dependencies": {
"@ohos/gpu_transform": "^1.0.2" "@ohos/gpu_transform": "^1.0.2"
}, },

View File

@ -16,6 +16,7 @@ import {
CacheStrategy, CacheStrategy,
DecodeImageInfo, DecodeImageInfo,
ErrorInfo, ErrorInfo,
FlipRotate,
ImageKnifeData, ImageKnifeData,
ImageKnifeRequestSource, ImageKnifeRequestSource,
ImageKnifeRequestWithSource, RequestJobRequest, ImageKnifeRequestWithSource, RequestJobRequest,
@ -164,6 +165,14 @@ export class ImageKnifeLoader {
} }
timeInfo.decodeStartTime = Date.now(); 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) await imageSource.createPixelMap(decodingOptions)
.then((pixelmap: PixelMap) => { .then((pixelmap: PixelMap) => {
timeInfo.decodeEndTime = Date.now(); timeInfo.decodeEndTime = Date.now();
@ -186,6 +195,17 @@ export class ImageKnifeLoader {
} catch (e) { } catch (e) {
LogUtil.error('PixelMap setTransferDetached failed:' + JSON.stringify(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);
}
//获取各个pixelMap的大小 //获取各个pixelMap的大小
if (resPixelmap !== undefined) { if (resPixelmap !== undefined) {
@ -253,6 +273,15 @@ export class ImageKnifeLoader {
return return
} }
timeInfo.decodeStartTime = Date.now(); timeInfo.decodeStartTime = Date.now();
// 获取旋转信息
let exif: string | undefined = undefined;
await imageSource.getImageProperty(image.PropertyKey.ORIENTATION).then((res)=>{
exif = res;
}).catch((error: BusinessError)=>{
LogUtil.info("Svg image don't have rotation information, " + error.message);
})
await imageSource.createPixelMap(opts) await imageSource.createPixelMap(opts)
.then((pixelmap: PixelMap) => { .then((pixelmap: PixelMap) => {
timeInfo.decodeEndTime = Date.now(); timeInfo.decodeEndTime = Date.now();
@ -260,6 +289,17 @@ export class ImageKnifeLoader {
imageSource.release() imageSource.release()
try { try {
resPixelmap.setTransferDetached(true) resPixelmap.setTransferDetached(true)
// 设置翻转和旋转角度
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('Svg image set flip , horizontal=' + result.horizontal + ', vertical=' +result.vertical + ', rotate=' + result.rotate);
}
} catch (e) { } catch (e) {
LogUtil.error('PixelMap setTransferDetached failed:' + JSON.stringify(e)) LogUtil.error('PixelMap setTransferDetached failed:' + JSON.stringify(e))
} }
@ -489,4 +529,39 @@ export class ImageKnifeLoader {
} }
} }
} }
static getOrientation(orientation: string | undefined){
let horizontal: boolean = false;
let vertical: boolean = false;
let rotate: number= 0;
switch (orientation){
case 'Top-left': break
case 'Top-right':
horizontal = true;
break;
case 'Bottom-left':
vertical = true;
break
case 'Bottom-right':
rotate = 180;
break;
case 'Left-top':
horizontal = true;
rotate = 270;
break
case 'Right-top':
rotate = 90;
break;
case 'Left-bottom':
rotate = 270;
break
case 'Right-bottom':
horizontal = true;
rotate = 90;
break;
}
let data: FlipRotate = { horizontal: horizontal, vertical: vertical, rotate:rotate };
return data
}
} }

View File

@ -158,3 +158,9 @@ export interface RequestJobRequest {
readTimeout?: number readTimeout?: number
} }
export interface FlipRotate{
horizontal: boolean,
vertical: boolean,
rotate: number,
}