目录

如何加载显示不同深度的图片数据

问题现象

对于深度BPP为1,或深度BPP为8的图片数据ArrayBuffer,应该如何转为图片显示?

背景知识

在计算机图形学中,BPP(Bits Per Pixel)代表每像素的颜色深度,也就是存储一个像素所需的位数。BPP越高,图像的颜色表现力越丰富,但所需的存储空间也越大。

具体来说,BPP值与图像中每个像素可以表示的颜色数量直接相关。例如,8BPP的图像每个像素使用8位来表示颜色,可以表示256种颜色。而24BPP的图像,每个像素使用24位,其中红、绿、蓝各8位,可以表示超过1600万种颜色,通常用于高质量的真彩色图像。

解决方案

使用图片数据创建PixelMap对象可使用image.createPixelMap,常用的深度BPP与图片像素的关系如下所示。

BPP

说明

输出格式

1

二值图(黑白图)

每个像素用1位表示是黑色还是白色。

8

灰度图

灰度图像,每个像素用8位表示,可表示256种灰度。

24

RGB彩色图

RGB 真彩色,每个像素用24位表示(8位红色+8位绿色+8位蓝色),可表示1677万种颜色,是目前最常见的图像格式之一。

32

ARGB彩色图

RGB真彩色加上Alpha通道(透明度),每个像素用32位表示,可以实现半透明效果。

将不同深度的图像数据转换为PixelMap,为了保证图片正常显示,需要确定图像数据宽度的准确性,将图像二进制数据ImageData保存在文件中并引用。参考如下:

import { image } from '@kit.ImageKit';
import { buffer } from '@kit.ArkTS';
import { imageData } from '../common/ImageData';

function convertBitmapArrayToPixels(readBuffer: ArrayBuffer, width: number, height: number, bpp: number) {
  let byteBuffer: Uint8Array = new Uint8Array(readBuffer);
  let byteBufferTemp: Uint8Array = new Uint8Array(new ArrayBuffer(readBuffer.byteLength));
  // 二值化黑白图,每个比特表示像素是黑色还是白色
  if (bpp == 1) {
    byteBufferTemp = new Uint8Array(new ArrayBuffer(byteBuffer.byteLength * 8 * 4));
    for (let index = 0; index < byteBuffer.length; index++) {
      for (let i = 0; i < 8; i++) {
        let value: number = (byteBuffer[index] >> (7 - i)) & 0x00000001;
        if (value == 1) {
          byteBufferTemp[(index * 8 + i) * 4] = 255;
          byteBufferTemp[(index * 8 + i) * 4 + 1] = 255;
          byteBufferTemp[(index * 8 + i) * 4 + 2] = 255;
          byteBufferTemp[(index * 8 + i) * 4 + 3] = 255;
        } else {
          byteBufferTemp[(index * 8 + i) * 4] = 0;
          byteBufferTemp[(index * 8 + i) * 4 + 1] = 0;
          byteBufferTemp[(index * 8 + i) * 4 + 2] = 0;
          byteBufferTemp[(index * 8 + i) * 4 + 3] = 255;
        }

      }
    }
  }
  // 灰度化灰度图,每个字节表示像素的灰度值
  if (bpp == 8) {
    byteBufferTemp = new Uint8Array(new ArrayBuffer(readBuffer.byteLength * 4));
    for (let index = 0; index < byteBuffer.length; index++) {
      let value = byteBuffer[index];
      byteBufferTemp[index * 4] = value;
      byteBufferTemp[index * 4 + 1] = value;
      byteBufferTemp[index * 4 + 2] = value;
      byteBufferTemp[index * 4 + 3] = 255;
    }
  }
  // RGB彩色图,每三个字节表示像素的rgb颜色
  if (bpp == 24) {
    byteBufferTemp = new Uint8Array(new ArrayBuffer(4 * byteBuffer.byteLength / 3));
    for (let i = 0; i < byteBuffer.length / 3; i++) {
      byteBufferTemp[i * 4] = byteBuffer[i * 3];
      byteBufferTemp[i * 4 + 1] = byteBuffer[i * 3 + 1];
      byteBufferTemp[i * 4 + 2] = byteBuffer[i * 3 + 2];
      byteBufferTemp[i * 4 + 3] = 255;
    }
  }
  let color: ArrayBuffer = byteBufferTemp.buffer as ArrayBuffer;
  let opts: image.InitializationOptions = { editable: true, pixelFormat: 3, size: { height: height, width: width } };
  let pixelMapTemp: image.PixelMap = image.createPixelMapSync(color, opts);
  return pixelMapTemp;
}

@Entry
@Component
struct BppPixelMapPage {
  @State pixelMapTemp: image.PixelMap | undefined = undefined;

  build() {
    Column() {
      Image(this.pixelMapTemp)
        .objectFit(ImageFit.Contain)
        .width('49%')
        .height(240);
      Button('读取图片')
        .width(150)
        .onClick(() => {
          // 文章图片数据仅作为示例,需要保证传入图片的宽高属性准确,否则会导致崩溃
          this.pixelMapTemp = convertBitmapArrayToPixels(buffer.from(imageData).buffer, 787, 1146, 8);
        });
    }
    .height('100%')
    .width('100%');
  }
}

总结

加载显示不同深度的图片数据,可参考以下步骤:

  1. 获取图片二进制数据,并写入缓冲区。
  2. 根据深度(BPP)不同,采取对应的像素数据处理。
  3. 将处理完成的像素数据通过image.createPixelMap创建新的PixelMap实例并展示。需要注意的是,创建PixelMap需要确保图片宽高属性正确,当宽高属性错误时,会因为跨距问题导致图像出现斜条纹。