文档管理中心

使用rotate接口对PixelMap多次旋转后图像逐渐变小

问题现象

使用Image组件显示PixelMap图像数据,对PixelMap图像使用rotate接口多次旋转后,图片逐渐变小,直到几乎消失不见。

背景知识

  • Image组件用于显示图片,支持加载PixelMap类型的数据源。objectFit属性用于设置图片的填充效果,当取值为ImageFit.Contain时将会对图片保持宽高比进行缩小或者放大,使得图片完全显示在显示边界内。
  • PixelMap是图像像素类,用于读取或写入图像数据以及获取图像信息。rotate接口会根据输入的角度对PixelMap进行旋转,当旋转的角度不是90的整数倍时,旋转后PixelMap的尺寸会发生改变。

问题定位

出现问题的核心代码如下:

  1. 使用Image组件显示PixelMap数据,组件id为'ImageContent',objectFit属性设置为ImageFit.Contain,确保图片完整显示。
  2. 旋转图片时,先获取Image组件的组件截图,对组件截图旋转相应的角度后,将旋转后的PixelMap赋值给状态变量currentPixelMap,显示在Image组件上。
    收起
    自动换行
    深色代码主题
    复制
    1. // 显示PixelMap图像
    2. Image(this.currentPixelMap)
    3. .id('ImageContent')
    4. .objectFit(ImageFit.Contain)
    5. // 旋转PixelMap图像的函数
    6. rotateImage: (rotate: number) => void = (rotate: number) => {
    7. let pixelMap = componentSnapshot.getSync('ImageContent'); // 获取Image组件的组件截图
    8. pixelMap?.rotateSync(rotate); // 对Image组件的截图旋转相应的角度
    9. this.currentPixelMap = pixelMap; // 用旋转后的组件截图刷新Image组件显示的PixelMap数据
    10. }

每次旋转时都是对Image组件截图,并对截图内容进行旋转,然后将旋转后的内容重新显示在Image组件上。存在以下问题:

  1. Image组件本身会对图像进行缩放,当objectFit属性设置为ImageFit.Contain时,会放大或者缩小图像使得图像完全显示在显示边界内,若图像本身的宽高比和Image组件的宽高比不一致时,图像不能正好完全铺满Image组件,这时对Image组件截图得到的PixelMap中图像只占一部分,另一部分是组件的背景。
  2. 使用rotate接口对图像进行旋转后,当旋转角度不是90的整数倍时,旋转后图像的尺寸会变大,这时旋转前的图像只占据了旋转后图像的一部分。如果对旋转后的图像继续旋转非90度的整数倍,那么图像的尺寸会再次变大,原始图像占据的部分会越来越小,显示在Image组件上时就会逐渐变小。

分析结论

在旋转图像时,旋转的不是原始图像,而是对Image组件的截图进行旋转,并且每次旋转都是在上一次旋转得到的图像上进行旋转,而旋转角度为非90度的整数倍时,旋转后的图像尺寸会发生变化,旋转前只占据旋转后图像的一部分,连续多次旋转后,原始图像在图像中占据的部分会越来越小,因此当显示在Image组件上时,看起来图像逐渐变小。

参考代码如下:

收起
自动换行
深色代码主题
复制
  1. import image from '@ohos.multimedia.image';
  2. import { Context } from '@kit.AbilityKit';
  3. @Entry
  4. @Component
  5. export struct PixelMapRotateSmallerDemo {
  6. @State rotationPixelMap: image.PixelMap | undefined = undefined; // 旋转PixelMap
  7. private pixelMap: image.PixelMap | undefined = undefined; // 原始PixelMap
  8. private rotation: number = 0; // 旋转角度
  9. async aboutToAppear(): Promise<void> {
  10. let imageSource: image.ImageSource | undefined;
  11. try {
  12. // 读取图片文件,这里以Rawfile目录下的img.png图片为例
  13. let context = this.getUIContext().getHostContext() as Context;
  14. let fileData: Uint8Array = context.resourceManager.getRawFileContentSync('img.png');
  15. // 解码图片文件为PixelMap
  16. imageSource = image.createImageSource(fileData.buffer.slice(0));
  17. this.pixelMap = await imageSource.createPixelMap();
  18. this.rotationPixelMap = await this.getPixelmap();
  19. } catch (err) {
  20. console.error(`Failed to load image, Cause: ${JSON.stringify(err)}`);
  21. } finally {
  22. if (imageSource) {
  23. await imageSource.release();
  24. }
  25. }
  26. }
  27. async getPixelmap() {
  28. if (this.pixelMap === undefined) {
  29. return undefined;
  30. }
  31. try {
  32. // 从原始PixelMap读取像素数据
  33. let bufferSize = this.pixelMap.getPixelBytesNumber();
  34. let pixelsBuffer = new ArrayBuffer(bufferSize);
  35. this.pixelMap.readPixelsToBufferSync(pixelsBuffer);
  36. let info = this.pixelMap.getImageInfoSync();
  37. // 创建新的PixelMap,确保每次都在原图基础上旋转
  38. let initOpts: image.InitializationOptions = {
  39. size: info.size,
  40. srcPixelFormat: info.pixelFormat,
  41. };
  42. let pixelMap = await image.createPixelMap(pixelsBuffer, initOpts);
  43. return pixelMap;
  44. } catch (err) {
  45. console.error(`Failed to copy pixelmap, Cause: ${JSON.stringify(err)}`);
  46. return undefined;
  47. }
  48. }
  49. async rotatePixelmap() {
  50. // 设置旋转角度,每次旋转30度
  51. this.rotation = (this.rotation + 30) % 360;
  52. let oldPixelMap: image.PixelMap | undefined;
  53. try {
  54. let newPixelMap = await this.getPixelmap();
  55. if (newPixelMap === undefined) {
  56. return;
  57. }
  58. // 旋转PixelMap
  59. newPixelMap.rotateSync(this.rotation);
  60. // 保存旧PixelMap
  61. oldPixelMap = this.rotationPixelMap;
  62. // 更新PixelMap
  63. this.rotationPixelMap = newPixelMap;
  64. } catch (err) {
  65. console.error(`failed to rotate image, Cause: ${JSON.stringify(err)}`);
  66. } finally {
  67. if (oldPixelMap) {
  68. oldPixelMap.release();
  69. }
  70. }
  71. }
  72. build() {
  73. Column({ space: 20 }) {
  74. Image(this.rotationPixelMap)
  75. .width('100%')
  76. .aspectRatio(1)
  77. .objectFit(ImageFit.None);
  78. Button() {
  79. Text('Rotate')
  80. .padding(8)
  81. .fontSize(30)
  82. .fontColor(Color.White);
  83. }
  84. .backgroundColor('#0A59F7')
  85. .onClick(() => {
  86. this.rotatePixelmap();
  87. });
  88. }
  89. .width('100%')
  90. .height('100%')
  91. .justifyContent(FlexAlign.Center);
  92. }
  93. }

修改建议

多次旋转图像时,确保每次旋转都是在原始图像的基础上旋转。