随着相机传感器技术的发展,越来越多设备支持高像素(如2亿像素)照片的拍摄。高像素图片意味着更高的图像分辨率,放大查看时图像细节更清晰,但文件体积、解码内存、渲染内存、网络传输成本和处理耗时也更高。应用在上传、分享、备份、编辑、预览和大图浏览等场景处理高像素图片时,应先确认业务是否需要原始分辨率,再选择获取高像素原图、降采样解码、区域解码或像素格式转换等方案。
适用场景
通常不需要直接使用高像素原图的场景
处理方式
可能原因
解决措施
可能原因
系统在部分访问链路中会将高像素图片转换为兼容性更好的JPEG图片,以降低应用处理成本并提升兼容性。转换后的图片通常约为1200万像素或约1250万像素,实际结果以系统实现和图片来源为准。
解决措施
API version 24开始提供AssetCompatibleCapability接口,支持配置应用是否兼容高像素图片资源。
如果应用需要获取高像素原图,应显式声明应用具备处理高像素图片的能力,并在后续处理流程中做好降采样、区域解码和内存管理。
通过PhotoPicker获取图片时,可在PhotoSelectOptions中配置assetCompatibleCapability以声明具备高像素原图的处理能力。
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { BusinessError } from '@kit.BasicServicesKit';
async function pickerExample(): Promise<void> {
try {
const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
photoSelectOptions.assetCompatibleCapability = {
supportedHighResolution: true
} as photoAccessHelper.AssetCompatibleCapability;
const photoPicker = new photoAccessHelper.PhotoViewPicker();
const photoSelectResult: photoAccessHelper.PhotoSelectResult =
await photoPicker.select(photoSelectOptions);
console.info(`PhotoViewPicker.select successfully: ${JSON.stringify(photoSelectResult)}`);
} catch (error) {
const err = error as BusinessError;
console.error(`PhotoViewPicker.select failed. code: ${err.code}, message: ${err.message}`);
}
} 对于网盘、备份等需要按高像素原图处理媒体文件的应用,可使用setAssetCompatibleCapability接口配置AssetCompatibleCapability。
import { dataSharePredicates } from '@kit.ArkData';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo as fs } from '@kit.CoreFileKit';
async function enableHighResolution(
phAccessHelper: photoAccessHelper.PhotoAccessHelper
): Promise<void> {
const capability: photoAccessHelper.AssetCompatibleCapability = {
supportedHighResolution: true
};
await phAccessHelper.setAssetCompatibleCapability(capability);
}
async function getHighResolutionAsset(
phAccessHelper: photoAccessHelper.PhotoAccessHelper
): Promise<void> {
try {
await enableHighResolution(phAccessHelper);
const predicates = new dataSharePredicates.DataSharePredicates();
const fetchOptions: photoAccessHelper.FetchOptions = {
fetchColumns: [photoAccessHelper.PhotoKeys.URI],
predicates
};
const fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
await phAccessHelper.getAssets(fetchOptions);
const asset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
const srcFile = fs.openSync(asset.uri, fs.OpenMode.READ_ONLY);
// 获取到fd后,可按业务需要进行解码、上传或备份。
console.info(`Open source file successfully. fd: ${srcFile.fd}`);
} catch (error) {
const err = error as BusinessError;
console.error(`Failed to get high resolution asset: code: ${err.code}, message: ${err.message}`);
}
} 可能原因
解决措施
示例(ArkTS):
解决获取图片格式异常问题,获取真实MIMEType。
import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';
function getImageMimeType(filePath: string): string {
try {
const source: image.ImageSource = image.createImageSource(filePath);
const info: image.ImageInfo = source.getImageInfoSync();
const mime: string = info.mimeType;
source.release();
return mime;
} catch (error) {
return "";
}
} 示例(C/C++):
解决获取图片格式异常问题,获取真实MIMEType。
在target_link_libraries依赖中添加libimage_source.so和libimage_common.so。
target_link_libraries(entry PUBLIC libimage_source.so libimage_common.so)
#include <cstdlib>
#include <cstring>
#include <multimedia/image_framework/image/image_source_native.h>
#include <multimedia/image_framework/image/image_common.h>
/**
* @brief 通过文件描述符获取图片MIME类型。
*
* 返回的字符串由调用者负责释放(使用 free())。
*
* @param fd 图片文件描述符,调用者负责打开和关闭fd。
* @return 成功时返回动态分配的MIME类型字符串;失败时返回nullptr。
*/
static char* GetImageMimeType(int32_t fd)
{
if (fd < 0) {
return nullptr;
}
OH_ImageSourceNative *source = nullptr;
Image_ErrorCode ret = OH_ImageSourceNative_CreateFromFd(fd, &source);
if (ret != IMAGE_SUCCESS || source == nullptr) {
return nullptr;
}
OH_ImageSource_Info *info = nullptr;
ret = OH_ImageSourceInfo_Create(&info);
if (ret != IMAGE_SUCCESS || info == nullptr) {
OH_ImageSourceNative_Release(source);
return nullptr;
}
ret = OH_ImageSourceNative_GetImageInfo(source, 0, info);
if (ret != IMAGE_SUCCESS) {
OH_ImageSourceInfo_Release(info);
OH_ImageSourceNative_Release(source);
return nullptr;
}
Image_MimeType mime = {nullptr, 0};
ret = OH_ImageSourceInfo_GetMimeType(info, &mime);
if (ret != IMAGE_SUCCESS || mime.data == nullptr) {
OH_ImageSourceInfo_Release(info);
OH_ImageSourceNative_Release(source);
return nullptr;
}
char *result = static_cast<char *>(malloc(mime.size + 1));
if (result == nullptr) {
OH_ImageSourceInfo_Release(info);
OH_ImageSourceNative_Release(source);
return nullptr;
}
memcpy(result, mime.data, mime.size);
result[mime.size] = '\0';
OH_ImageSourceInfo_Release(info);
OH_ImageSourceNative_Release(source);
return result;
} 判断方式
应用可通过图片宽高计算像素总数,并结合业务阈值判断图片是否属于高像素图片。本文示例以width * height >= 100000000作为高像素图片判断条件,即图片像素总数不小于1亿。
该阈值不是固定标准,主要用于识别带来较高解码和渲染内存压力的图片。应用可根据目标设备内存、页面显示尺寸、性能指标和业务体验要求调整阈值。
需要注意,只有接入高像素原图获取后拿到的图片宽高才是原始图片的宽高,否则获取到的是转换后兼容性文件的宽高。接入方式可参考为什么通过PhotoPicker或URI获取高像素图片时得到约1200万像素或约1250万像素的JPEG图片?。
示例(ArkTS):
import { image } from '@kit.ImageKit';
const HIGH_PIXEL_THRESHOLD: number = 100000000;
function isHighPixelImage(fd: number): boolean {
let imageSource: image.ImageSource | undefined = undefined;
try {
imageSource = image.createImageSource(fd);
const imageInfo: image.ImageInfo = imageSource.getImageInfoSync();
const width: number = imageInfo.size.width;
const height: number = imageInfo.size.height;
const pixelCount: number = width * height;
return pixelCount >= HIGH_PIXEL_THRESHOLD;
} catch (error) {
return false;
} finally {
imageSource?.release();
}
} 示例(C/C++):
在target_link_libraries依赖中添加libimage_source.so。
target_link_libraries(entry PUBLIC libimage_source.so)
#include <cstdint>
#include <multimedia/image_framework/image/image_common.h>
#include <multimedia/image_framework/image/image_source_native.h>
static constexpr uint64_t HIGH_PIXEL_THRESHOLD = 100000000ULL;
/**
* @brief 判断图片是否为高像素图。
*
* 判断规则:
* width * height >= 100000000。
*
* @param fd 图片文件fd,调用者负责打开和关闭fd。
* @return true 表示高像素图。
* @return false 表示不是高像素图,或获取图片信息失败。
*/
static bool IsHighPixelImage(int32_t fd)
{
if (fd < 0) {
return false;
}
OH_ImageSourceNative *imageSource = nullptr;
Image_ErrorCode ret = OH_ImageSourceNative_CreateFromFd(fd, &imageSource);
if (ret != IMAGE_SUCCESS || imageSource == nullptr) {
return false;
}
OH_ImageSource_Info *imageInfo = nullptr;
ret = OH_ImageSourceInfo_Create(&imageInfo);
if (ret != IMAGE_SUCCESS || imageInfo == nullptr) {
OH_ImageSourceNative_Release(imageSource);
return false;
}
ret = OH_ImageSourceNative_GetImageInfo(imageSource, 0, imageInfo);
if (ret != IMAGE_SUCCESS) {
OH_ImageSourceInfo_Release(imageInfo);
OH_ImageSourceNative_Release(imageSource);
return false;
}
uint32_t width = 0;
uint32_t height = 0;
ret = OH_ImageSourceInfo_GetWidth(imageInfo, &width);
if (ret != IMAGE_SUCCESS) {
OH_ImageSourceInfo_Release(imageInfo);
OH_ImageSourceNative_Release(imageSource);
return false;
}
ret = OH_ImageSourceInfo_GetHeight(imageInfo, &height);
if (ret != IMAGE_SUCCESS) {
OH_ImageSourceInfo_Release(imageInfo);
OH_ImageSourceNative_Release(imageSource);
return false;
}
uint64_t pixelCount = static_cast<uint64_t>(width) * static_cast<uint64_t>(height);
bool isHighPixel = pixelCount >= HIGH_PIXEL_THRESHOLD;
OH_ImageSourceInfo_Release(imageInfo);
OH_ImageSourceNative_Release(imageSource);
return isHighPixel;
} 适用场景
图片解码接口在未指定desiredPixelFormat时,默认会按RGBA_8888显示链路生成PixelMap。RGBA_8888适合图片显示、编辑和通用图像处理,但高像素图片按RGBA_8888解码时,内存通常按width * height * 4估算。
如果业务不需要透明通道,可根据后续处理链路选择更合适的格式:
| 解码像素格式 | 适用场景 | 内存估算(受硬件对齐影响,实际内存占用可能更高) |
|---|---|---|
| image.PixelMapFormat.RGBA_8888 | 通用显示、编辑,或需要透明通道与高色彩精度的场景。 | 约4字节/像素。 |
| image.PixelMapFormat.RGB_565 | 无透明通道需求且色彩精度要求较低的预览、缩略图、列表图片等场景。 | 约2字节/像素。 |
| image.PixelMapFormat.NV21、image.PixelMapFormat.NV12 | 无透明通道需求,且后续需接入相机、视频、算法或硬件编解码等 YUV 处理链路的场景。 | 约2字节/像素。 |
处理方式
示例:
import { image } from '@kit.ImageKit';
const decodingOptions: image.DecodingOptions = {
desiredSize: { width: 0, height: 0 }, // 这是desiredSize的默认值,在该配置下会按照原图尺寸解码。
desiredPixelFormat: image.PixelMapFormat.RGB_565 // 可根据具体需求配置不同解码格式。
}; 适用场景
针对列表预览、详情页首屏、分享预览等仅需屏幕展示的场景,建议优先使用降采样解码。当业务涉及大图浏览、手势缩放或局部细节查看(如自定义图片查看器)时,应在降采样基础上叠加区域解码,以兼顾显示效果与内存安全。
处理方式
降采样解码与区域解码示例请参考图片区域解码与下采样(C/C++)或图片区域解码与下采样(ArkTS)。