思路:
使用OffScreenCanvas离屏画布拿到图片源数据,在离屏画布上使用OffscreenCanvasRenderingContext2D的 drawImage和 fillText分别绘制图像和文水印文字。水印图片的保存可以使用安全控件SaveButton结合fs.write来实现。
一、添加水印流程和方法
1.解析图片得到pixelMap数据。
async getImagePixelMap(resource: Resource): Promise<ImagePixelMap> {
const data: Uint8Array = await getContext(this).resourceManager.getMediaContent(resource);
const arrayBuffer: ArrayBuffer = data.buffer.slice(data.byteOffset, data.byteLength + data.byteOffset);
const imageSource: image.ImageSource = image.createImageSource(arrayBuffer);
return await imageSource2PixelMap(imageSource)
}
2.使用ImageSource.getImageInfo()方法获取图片宽、高信息,使用ImageSource.createPixelMap()方法创建PixelMap对象。
export async function imageSource2PixelMap(imageSource: image.ImageSource): Promise<ImagePixelMap> {
const imageInfo: image.ImageInfo = await imageSource.getImageInfo();
const height = imageInfo.size.height;
const width = imageInfo.size.width;
const options: image.DecodingOptions = {
editable: true,
desiredSize: { height, width }
};
const pixelMap: PixelMap = await imageSource.createPixelMap(options);
const result: ImagePixelMap = { pixelMap, width, height };
return result;
}
export interface ImagePixelMap {
pixelMap: image.PixelMap
width: number
height: number
}
3.封装添加水印的方法
export function addWatermark(
imagePixelMap: ImagePixelMap,
text: string = '自定义水印',
text2: string = '自定义水印时间',
drawWatermark?: (OffscreenContext: OffscreenCanvasRenderingContext2D) => void
): image.PixelMap {
const height = px2vp(imagePixelMap.height);
const width = px2vp(imagePixelMap.width);
const offScreenCanvas = new OffscreenCanvas(width, height);
const offScreenContext = offScreenCanvas.getContext('2d');
offScreenContext.drawImage(imagePixelMap.pixelMap, 0, 0, width, height);
if (drawWatermark) {
drawWatermark(offScreenContext);
} else {
const imageScale = width / px2vp(display.getDefaultDisplaySync().width);
offScreenContext.textAlign = 'right';
offScreenContext.fillStyle = '#A2FFFFFF';
offScreenContext.font = 16 * imageScale + 'vp';
const padding = 5 * imageScale;
offScreenContext.fillText(text, width - padding, height - padding - 18);
offScreenContext.fillText(text2, width - padding, height - padding);
}
return offScreenContext.getPixelMap(0, 0, width, height);
}
4.封装下载图片的方法
export async function saveToFile(pixelMap: image.PixelMap, context: Context) {
try {
const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context)
const filePath = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'png')
// AlertDialog.show({ message: JSON.stringify(filePath, null, 2) })
const imagePacker = image.createImagePacker()
const imageBuffer = await imagePacker.packing(pixelMap, {
format: 'image/png',
quality: 80
})
const mode = fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE;
fd = (await fileIo.open(filePath, mode)).fd;
await fileIo.truncate(fd);
await fileIo.write(fd, imageBuffer);
} catch (err) {
hilog.error(0x0000, TAG, 'saveToFile error:', JSON.stringify(err) ?? '');
// AlertDialog.show({ message: JSON.stringify(err, null, 2) })
} finally {
if (fd) {
fileIo.close(fd);
}
}
}
二、调用方法实现添加水印
@State addedWatermarkPixelMap: image.PixelMap | null = null;
async aboutToAppear() {
const imagePixelMap = await this.getImagePixelMap($r('app.media.img1'))
//显示图片
this.addedWatermarkPixelMap = addWatermark(imagePixelMap)
}
三、使用安全控件SaveButton保存图片到相册
SaveButton({ text: SaveDescription.SAVE })
.backgroundColor('#00ffffff')
.fontColor(Color.Black)
.onClick(async () => {
try {
if (this.addedWatermarkPixelMap === null) {
return
}
await saveToFile(this.addedWatermarkPixelMap, getContext(this))
} catch (err) {
AlertDialog.show({ message: JSON.stringify('保存失败', null, 2) })
}
})