鸿蒙PixelMap(Bitmap)使用
前言
上一篇文章在本地视频里面获取了第一帧的图片,得到的是一个PixelMap,其实就是安卓里面的Bitmap,在上篇文章里面我还将这个Bitmap保存到了文件中。
这篇文章的内容和PixelMap相关,我就直接连着发了,就是一些对PixelMap的操作。
PixelMap官方文档
PixelMap也没什么好说的,感觉比安卓的Bitmap少了好多东西,当然也可能我了解的不多,看官方文档吧:
配合Canvas使用
在安卓中,我们对Bitmap的使用经常带着Canvas,在鸿蒙中找了我好久,终于找到了同样的东西,就是OffscreenCanvas,这个也可以去看官方文档:
其实说一千道一万,直接写个例子就很简单了:
ts 代码解读复制代码 /**
* 根据水印文字,生成斜着排列多层mask的遮罩图片
*
* @param w
* @param h
* @param mark
* @param alpha
* @returns
*/
static createTextWatermark(w: number, h: number, mark: string, alpha: number) {
// 通过 OffscreenCanvasRenderingContext2D 绘制水印
const offScreenCanvas = new OffscreenCanvas(w, h);
const offScreenContext: OffscreenCanvasRenderingContext2D = offScreenCanvas.getContext('2d');
// 透明度
offScreenContext.globalAlpha = alpha / 100;
offScreenContext.textAlign = 'start';
offScreenContext.textBaseline = 'bottom';
offScreenContext.fillStyle = '#000000';
// 设置字体大小
offScreenContext.font = '60px sans-serif';
// 添加文字阴影
offScreenContext.shadowBlur = 20;
offScreenContext.shadowColor = '#F3F3F3';
// 在左边的中间位置开始添加水印
let cols = 20;
let rows = 10;
// 测量宽度
let markWidth = offScreenContext.measureText(mark).width + 100;
// 弧度
let v = 45 * (Math.PI / 180);
// 旋转后宽高
let chunkHeight = Math.sin(v) * markWidth;
let chunkWidth = Math.cos(v) * markWidth;
offScreenContext.save();
offScreenContext.rotate(-v);
for (let k = -5; k < cols; k = (k + 2)) {
for (let i = -5; i < rows; i = (i + 2)) {
// 绘制文本
offScreenContext.fillText(mark, chunkWidth * k, chunkHeight * i);
}
}
offScreenContext.restore();
// 生成新位图
let bmp = offScreenContext.getPixelMap(0, 0, offScreenCanvas.width, offScreenCanvas.height);
return bmp;
}
这是我写的一个绘制文字水印的例子,效果不是很好,但是凑合看吧。可以看到像save、rotate、restore方法都有,和Android的canvas还是挺像的,不过这明显就是前端的canvas,凑合用吧。
上面是文字的,下面在手写个和PixelMap相关的水印的:
ts 代码解读复制代码 /**
* 根据水印图片,生成斜着排列多层mask的遮罩图片
*
* @param w
* @param h
* @param mask
* @param alpha
* @returns
*/
static createImgWatermark(w: number, h: number, mask: PixelMap, alpha: number): PixelMap {
let maskInfo = mask.getImageInfoSync();
// 旋转
mask.rotateSync(-45);
// 缩放
let scale = w / 2.0 / maskInfo.size.width;
mask.scaleSync(scale, scale);
// 通过 OffscreenCanvasRenderingContext2D 绘制水印
const offScreenCanvas = new OffscreenCanvas(w, h);
const offScreenContext: OffscreenCanvasRenderingContext2D = offScreenCanvas.getContext('2d');
// 设置透明度
offScreenContext.globalAlpha = alpha / 100;
let count = 0;
while (count * w < h) {
offScreenContext.drawImage(mask, w / 4 - maskInfo.size.width / 2, count * w);
offScreenContext.drawImage(mask, w / 4 * 3 - maskInfo.size.width / 2, (count + 0.5) * w);
count++;
}
// 生成新位图
let bmp = offScreenContext.getPixelMap(0, 0, offScreenCanvas.width, offScreenCanvas.height);
return bmp;
}
其实都差不多,没啥区别。
获取PixelMap
这个获取PixelMap可能比较多,我这写了两个方法,一个从本地文件获取,一个从资源文件获取:
ts 代码解读复制代码 /**
* 从文件中获取位图
*
* @param path 本地路径
* @returns
*/
async getPixelMapFromFile(path: string): Promise<PixelMap> {
return new Promise(async (resolve, reject) => {
try {
// 从文件创建
let file = await fs.open(path, fs.OpenMode.READ_ONLY);
const imageSource: image.ImageSource = image.createImageSource(file.fd);
// 配置
let decodingOptions: image.DecodingOptions = {
editable: true,
desiredPixelFormat: 3,
}
// 获取位图
imageSource.createPixelMap(decodingOptions, (err: BusinessError, pixelMap: PixelMap) => {
if (err !== undefined) {
LogUtil.e("createPixelMap fail: " + err.message);
reject("createPixelMap fail: " + err.message);
} else {
resolve(pixelMap);
LogUtil.d("getPixelMapFromFile success!")
}
})
} catch (e) {
LogUtil.e("pixelMap2Base64 error: " + e)
reject(e)
}
});
}
/**
* 从rawfile中获取位图
*
* @param path 本地路径
* @returns
*/
async getPixelMapFromRawFile(context: Context, name: string): Promise<PixelMap> {
return new Promise(async (resolve, reject) => {
try {
// 获取resourceManager资源管理器
const resourceMgr = context.resourceManager;
const fileData = await resourceMgr.getRawFileContent(name);
// 获取图片的ArrayBuffer
const buffer = fileData.buffer;
const imageSource = image.createImageSource(buffer);
// 配置
let decodingOptions: image.DecodingOptions = {
editable: true,
desiredPixelFormat: 3,
}
// 创建pixelMap并进行简单的旋转和缩放
// 获取位图
imageSource.createPixelMap(decodingOptions, (err: BusinessError, pixelMap: PixelMap) => {
if (err !== undefined) {
LogUtil.e("createPixelMap fail: " + err.message);
reject("createPixelMap fail: " + err.message);
} else {
resolve(pixelMap);
LogUtil.d("getPixelMapFromFile success!")
}
})
} catch (e) {
LogUtil.e("pixelMap2Base64 error: " + e)
reject(e)
}
});
}
PixelMap导出
导出的功能上一篇文章也写了,这里再贴一下:
ts 代码解读复制代码 /**
* 保存pixelMap,返回路径
* @param pm
* @returns
*/
private async savePixelMap(context: Context, pm: PixelMap): Promise<string> {
if (pm === null) {
LogUtil.e('传入的pm为空');
return '';
}
try {
return await this.packToFile(context, pm);
} catch (err) {
LogUtil.e("TAG", '保存文件失败,err=' + JSON.stringify(err));
return '';
}
}
private async packToFile(context: Context, pixelMap: PixelMap): Promise<string> {
let fPath: string = context.cacheDir + '/image/' + this.getTimeStr() + '.jpg';
let writeFd: fs.File = await fs.open(fPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
let opts : image.PackingOption = { format: "image/jpeg", quality: 100};
const imagePacker = image.createImagePacker();
await imagePacker.packToFile(pixelMap, writeFd.fd, opts);
fs.closeSync(writeFd.fd);
return fPath;
}
private getTimeStr() {
const now: Date = new Date();
const year: number = now.getFullYear();
const month: number = now.getMonth() + 1;
const day: number = now.getDate();
const hours: number = now.getHours();
const minutes: number = now.getMinutes();
const seconds: number = now.getSeconds();
return `${year}${month}${day}_${hours}${minutes}${seconds}`;
}
PixelMap和Base64
有时候还要处理下base图片相关的内容,这里贴一下我用到的几个:
ts 代码解读复制代码 async saveBase64Image(base64ImageData: string, context: Context): Promise<string> {
try {
let base64Result = this.dealBase64Str(base64ImageData)
let file = this.createFile(context)
let bufferImage = buffer.from(base64Result, 'base64')
await fs.write(file.fd, bufferImage.buffer)
fs.closeSync(file.fd)
return file.path;
} catch (e) {
LogUtil.e("saveBase64Image error: " + JSON.stringify(e));
}
return ""
}
private dealBase64Str(base64Data: string): string {
let imageData: string
if (base64Data.startsWith("data")) {
const base64Split: string[] = base64Data.split(",")
if (base64Split.length !== 2) {
throw new Error(`Illegal base64 data`)
}
imageData = base64Split[1].trim()
} else {
imageData = base64Data
}
return imageData
}
/**
* 将pixelMap转成base64格式
*
* @param pixelMap
* @returns
*/
async pixelMap2Base64(pixelMap: PixelMap): Promise<string> {
return new Promise((resolve, reject) => {
try {
// 转换成base64
const imagePackerApi: image.ImagePacker = image.createImagePacker();
let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 100 };
imagePackerApi.packing(pixelMap, packOpts).then((data: ArrayBuffer) => {
let buf: buffer.Buffer = buffer.from(data);
let base64 = 'data:image/jpeg;base64,' + buf.toString('base64', 0, buf.length);
resolve(base64);
});
} catch (e) {
LogUtil.e("pixelMap2Base64 error: " + e)
reject(e)
}
});
}
private createFile(context: Context) {
let fPath: string = context.cacheDir + '/image/' + this.getTimeStr() + '.jpg';
let dir = context.cacheDir + '/image';
if (!fs.accessSync(dir)) {
fs.mkdirSync(dir)
}
let file = fs.openSync(fPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
return file
}
小结
简单地介绍了下鸿蒙里面PixelMap(Bitmap)地使用,给出了两个和安卓Canvas使用类似地例子,也给出了一下PixelMap常用到地工具方法。
评论记录:
回复评论: