本文同步发表于微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一、位图操作

位图操作指对PixelMap进行像素级的读写操作,可以对目标图片中的部分区域进行处理。

应用场景

场景 说明
图片美化 调整局部颜色、亮度
添加水印 在指定区域写入文字或Logo
滤镜效果 批量修改像素颜色
图像拼接 将多张图片合并成一张

操作示意图

原图:完整图片
    ↓
读取指定矩形区域像素数据
    ↓
修改像素数据
    ↓
写回原图片对应区域
    ↓
得到修改后的图片

二、导入模块

import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';

三、获取PixelMap信息

在进行位图操作前,需要先完成图片解码,获取PixelMap对象。

// 获取图像像素的总字节数
let pixelBytesNumber: number = pixelMap.getPixelBytesNumber();

// 获取图像像素每行字节数
let rowBytes: number = pixelMap.getBytesNumberPerRow();

// 获取当前图像像素密度(每英寸像素数量,密度越大图片越精细)
let density: number = pixelMap.getDensity();

四、像素读写操作

4.1 场景一:整张图片读写(Buffer方式)

使用readPixelsToBuffer读取整张图片像素数据到缓冲区,使用writeBufferToPixels将缓冲区数据写回。

// 按照PixelMap的像素格式,读取PixelMap的图像像素数据,并写入缓冲区中
const buffer = new ArrayBuffer(pixelBytesNumber);

pixelMap.readPixelsToBuffer(buffer).then(() => {
    console.info('Succeeded in reading image pixel data.');
}).catch((error: BusinessError) => {
    console.error('Failed to read image pixel data. The error is: ' + error);
});

// 修改缓冲区数据(如调整颜色、亮度等)
// ...

// 按照PixelMap的像素格式,读取缓冲区中的图像像素数据,并写入PixelMap
pixelMap.writeBufferToPixels(buffer).then(() => {
    console.info('Succeeded in writing image pixel data.');
}).catch((error: BusinessError) => {
    console.error('Failed to write image pixel data. The error is: ' + error);
});

4.2 场景二:指定区域图片读写(Pixels方式)

使用readPixels读取指定区域的像素数据,使用writePixels将数据写回指定区域。

PositionArea参数说明

参数 类型 说明
pixels ArrayBuffer 像素数据缓冲区
offset number 缓冲区偏移量
stride number 行步长(每行字节数)
region Region 区域信息(x, y, width, height)
// 固定按照BGRA_8888格式,读取PixelMap指定区域内的图像像素数据
const area: image.PositionArea = {
    pixels: new ArrayBuffer(8),
    offset: 0,
    stride: 8,
    region: { size: { height: 1, width: 2 }, x: 0, y: 0 }
};

pixelMap.readPixels(area).then(() => {
    console.info('Succeeded in reading the image data in the area.');
}).catch((error: BusinessError) => {
    console.error('Failed to read the image data in the area. The error is: ' + error);
});

// 修改区域像素数据
// ...

// 固定按照BGRA_8888格式,将缓冲区中的图像像素数据写入PixelMap指定区域
pixelMap.writePixels(area).then(() => {
    console.info('Succeeded in writing the image data in the area.');
}).catch((error: BusinessError) => {
    console.error('Failed to write the image data in the area. The error is: ' + error);
});

说明

  • 建议readPixelsToBufferwriteBufferToPixels成对使用

  • readPixelswritePixels成对使用

  • 避免因图像像素格式不一致,造成PixelMap图像出现异常

五、示例

5.1 复制(深拷贝)位图并改变像素格式

该方法仅可实现PixelMap基本内容的复制,不支持复制色域和HDR元数据

提示:如果不需要改变新PixelMap的像素格式,请使用clonecloneSync方法。

/**
 * 复制(深拷贝)PixelMap并改变像素格式
 * 
 * @param pixelMap - 被复制的原PixelMap
 * @param desiredPixelFormat - 新PixelMap的像素格式(可选,不指定则沿用原格式)
 * @returns 新PixelMap的Promise
 */
async function clonePixelMap(pixelMap: image.PixelMap, desiredPixelFormat?: image.PixelMapFormat): Promise<image.PixelMap> {
    // 获取原PixelMap的图片信息
    const imageInfo = pixelMap.getImageInfoSync();
    
    // 读取原PixelMap的像素数据,并按照原PixelMap的像素格式写入缓冲区
    const buffer = new ArrayBuffer(pixelMap.getPixelBytesNumber());
    await pixelMap.readPixelsToBuffer(buffer);
    
    // 根据原PixelMap的图片信息,生成初始化选项
    const options: image.InitializationOptions = {
        // 数据源的像素格式:必须匹配原PixelMap的像素格式
        srcPixelFormat: imageInfo.pixelFormat,
        // 新PixelMap的像素格式
        pixelFormat: desiredPixelFormat || imageInfo.pixelFormat,
        // 新PixelMap的透明度类型
        alphaType: imageInfo.alphaType,
        // 新PixelMap的尺寸:必须匹配原PixelMap的尺寸
        size: imageInfo.size
    };
    
    // 根据像素数据和初始化选项,创建新PixelMap
    return await image.createPixelMap(buffer, options);
}

限制说明

限制项 说明
不支持复制 色域、HDR元数据
不支持转换为 RGBA_1010102、YCBCR_P010、YCRCB_P010、ASTC_4x4

5.2 将两张宽度相同的位图纵向拼接成一张长图

限制:仅支持以下像素格式的PixelMap:RGBA_8888、BGRA_8888、RGBA_F16

async function concatPixelMap(pixelMap1: image.PixelMap, pixelMap2: image.PixelMap): Promise<image.PixelMap> {
    // 1. 将pixelMap1的像素数据读取至area1.pixels中
    const imageInfo1 = pixelMap1.getImageInfoSync();
    const area1: image.PositionArea = {
        pixels: new ArrayBuffer(pixelMap1.getPixelBytesNumber()),
        offset: 0,
        stride: pixelMap1.getBytesNumberPerRow(),
        region: {
            size: imageInfo1.size,
            x: 0,
            y: 0
        }
    };
    await pixelMap1.readPixels(area1);
    
    // 2. 将pixelMap2的像素数据读取至area2.pixels中
    const imageInfo2 = pixelMap2.getImageInfoSync();
    const area2: image.PositionArea = {
        pixels: new ArrayBuffer(pixelMap2.getPixelBytesNumber()),
        offset: 0,
        stride: pixelMap2.getBytesNumberPerRow(),
        region: {
            size: imageInfo2.size,
            x: 0,
            y: 0
        }
    };
    await pixelMap2.readPixels(area2);
    
    // 3. 创建一个新的空白PixelMap
    // 宽度与pixelMap1和pixelMap2相等,高度为两者相加
    const options: image.InitializationOptions = {
        srcPixelFormat: imageInfo1.pixelFormat,
        pixelFormat: imageInfo1.pixelFormat,
        size: {
            width: imageInfo1.size.width,
            height: imageInfo1.size.height + imageInfo2.size.height
        }
    };
    const newPixelMap = image.createPixelMapSync(options);
    
    // 4. 将像素数据按顺序写入新PixelMap
    await newPixelMap.writePixels(area1);
    
    // pixelMap2像素的写入位置应该从pixelMap1末行像素的下一行开始
    area2.region.y = imageInfo1.size.height;
    await newPixelMap.writePixels(area2);
    
    return newPixelMap;
}

总结

操作类型 方法 说明
获取像素字节数 getPixelBytesNumber() 图像像素总字节数
获取每行字节数 getBytesNumberPerRow() 每行像素字节数
获取像素密度 getDensity() 每英寸像素数
整张图片读取 readPixelsToBuffer() 读取到ArrayBuffer
整张图片写入 writeBufferToPixels() 从ArrayBuffer写入
区域读取 readPixels() 读取指定区域到PositionArea
区域写入 writePixels() 从PositionArea写入指定区域
深拷贝 clonePixelMap() 复制并改变像素格式
图像拼接 concatPixelMap() 两张图片纵向拼接

开发流程

完成图片解码获取PixelMap
    ↓
获取PixelMap信息(可选)
    ↓
读取像素数据(readPixelsToBuffer / readPixels)
    ↓
修改像素数据
    ↓
写回PixelMap(writeBufferToPixels / writePixels)
    ↓
使用修改后的PixelMap
    ↓
释放资源

   鸿蒙PixelMap位图操作通过readPixelsToBuffer/writeBufferToPixels(整图)和readPixels/writePixels(区域)实现像素级读写,支持深拷贝改变像素格式和图像拼接,是图片美化、水印添加等功能的基础。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐