支持图片携带的EXIF元数据作为显示方向
Signed-off-by: yang <yangweiping7@h-partners.com>
This commit is contained in:
parent
acb45a66bc
commit
115118e238
|
@ -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
|
||||||
|
|
|
@ -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%')
|
||||||
|
|
|
@ -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%')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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°"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -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 |
|
@ -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°"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -158,3 +158,9 @@ export interface RequestJobRequest {
|
||||||
readTimeout?: number
|
readTimeout?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FlipRotate{
|
||||||
|
horizontal: boolean,
|
||||||
|
vertical: boolean,
|
||||||
|
rotate: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue