往期知识点整理
- 【HarmonyOS 鸿蒙实战开发】NavDestination弹窗
- 【HarmonyOS 鸿蒙实战开发】全局自定义组件复用实现案例
- 【HarmonyOS 鸿蒙实战开发】在TaskPool线程中操作关系型数据库实现案例
- 【HarmonyOS 鸿蒙实战开发】 发短信案例
- 【HarmonyOS 鸿蒙实战开发】 骨架屏实现案例
- 【HarmonyOS 鸿蒙实战开发】 画笔调色板
- 【HarmonyOS 鸿蒙实战开发】图片编辑实现马赛克效果
- 持续更新中……
介绍
本示例将原图手指划过的区域分割成若干个大小一致的小方格,然后获取每个小方格中的像素点的平均色彩数值,使用获取到的平均色彩数值替换该方格中所有的像素点。最后使用createPixelMapSync接口将新的像素点数据写入图片,即可实现原始图片的局部马赛克处理。
效果图预览
使用说明
- 进入页面,手指划过图片的某一个区域即可将该区域马赛克处理。点击底部的“恢复原图”按钮,将恢复为原图。
实现思路
- 获取原始图片信息,将原始图片设置为可编辑状态。
/**
* 获取图片内容
*/
@Concurrent
async function getImageContent(imgPath: string, context: Context): Promise<Uint8Array | undefined> {
// 获取resourceManager资源管理
const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
// 获取rawfile中的图片资源
const fileData: Uint8Array = await resourceMgr.getRawFileContent(imgPath);
return fileData;
}
/**
* 获取原始图片信息
*/
async getSrcImageInfo(): Promise<void> {
// TODO: 性能知识点:使用new taskpool.Task()创建任务项,传入获取图片内容函数和所需参数
const task: taskpool.Task = new taskpool.Task(getImageContent, MosaicConstants.RAWFILE_PICPATH, getContext(this));
try {
const fileData: Uint8Array = await taskpool.execute(task) as Uint8Array;
// 获取图片的ArrayBuffer
const buffer = fileData.buffer.slice(fileData.byteOffset, fileData.byteLength + fileData.byteOffset);
// 获取原图imageSource
this.imageSource = image.createImageSource(buffer);
// TODO 知识点: 将图片设置为可编辑
const decodingOptions: image.DecodingOptions = {
editable: true,
desiredPixelFormat: image.PixelMapFormat.RGBA_8888,
}
// 创建PixelMap
this.pixelMapSrc = await this.imageSource.createPixelMap(decodingOptions);
} catch (err) {
console.error("getSrcImageInfo: execute fail, err:" + (err as BusinessError).toString());
}
}
- 保存图片的原始尺寸及在屏幕的显示区域。
// 读取图片信息
const imageInfo: image.ImageInfo = await this.pixelMapSrc!.getImageInfo();
// 获取图片的宽度和高度
this.imageWidth = imageInfo.size.width;
this.imageHeight = imageInfo.size.height;
// 获取屏幕尺寸
const displayData: display.Display = display.getDefaultDisplaySync();
// 计算图片的显示尺寸
this.displayWidth = px2vp(displayData.width);
this.displayHeight = this.displayWidth * this.imageHeight / this.imageWidth;
- 获取手指按下和移动时的坐标,手指移动时执行马赛克任务。
PanGesture()
.onActionStart((event: GestureEvent) => {
const finger: FingerInfo = event.fingerList[0];
if (finger == undefined) {
return;
}
this.startX = finger.localX;
this.startY = finger.localY;
})
.onActionUpdate((event: GestureEvent) => {
const finger: FingerInfo = event.fingerList[0];
if (finger == undefined) {
return;
}
this.endX = finger.localX;
this.endY = finger.localY;
// 执行马赛克任务
await this.doMosaicTask(this.startX, this.startY, this.endX, this.endY);
this.startX = this.endX;
this.startY = this.endY;
})
- 在马赛克任务中处理坐标转换问题后执行马赛克处理函数applyMosaic。
async doMosaicTask(offMinX: number, offMinY: number, offMaxX: number, offMaxY: number): Promise<void> {
// TODO 知识点:将手势移动的起始坐标转换为原始图片中的坐标
offMinX = Math.round(offMinX * this.imageWidth / this.displayWidth);
offMinY = Math.round(offMinY * this.imageHeight / this.displayHeight);
offMaxX = Math.round(offMaxX * this.imageWidth / this.displayWidth);
offMaxY = Math.round(offMaxY * this.imageHeight / this.displayHeight);
// 处理起始坐标大于终点坐标的情况
if (offMinX > offMaxX) {
const temp = offMinX;
offMinX = offMaxX;
offMaxX = temp;
}
if (offMinY > offMaxY) {
const temp = offMinY;
offMinY = offMaxY;
offMaxY = temp;
}
// 获取像素数据的字节数
const bufferData = new ArrayBuffer(this.pixelMapSrc!.getPixelBytesNumber());
await this.pixelMapSrc!.readPixelsToBuffer(bufferData);
// 将像素数据转换为 Uint8Array 便于像素处理
let dataArray = new Uint8Array(bufferData);
// TODO: 性能知识点:使用new taskpool.Task()创建任务项,传入任务执行函数和所需参数
const task: taskpool.Task =
new taskpool.Task(applyMosaic, dataArray, this.imageWidth, this.imageHeight, MosaicConstants.BLOCK_SIZE,
offMinX, offMinY, offMaxX, offMaxY);
try {
taskpool.execute(task, taskpool.Priority.HIGH).then(async (res: Object) => {
this.pixelMapSrc = image.createPixelMapSync((res as Uint8Array).buffer, this.opts);
this.isMosaic = true;
})
} catch (err) {
console.error("doMosaicTask: execute fail, " + (err as BusinessError).toString());
}
}
- 实现图像局部马赛克处理函数
async applyMosaic(dataArray: Uint8Array, imageWidth: number, imageHeight: number, blockSize: number,
offMinX: number, offMinY: number, offMaxX: number, offMaxY: number): Promise<Uint8Array | undefined> {
try {
// 计算横排和纵排的块数
let xBlocks = Math.floor((Math.abs(offMaxX - offMinX)) / blockSize);
let yBlocks = Math.floor((Math.abs(offMaxY - offMinY)) / blockSize);
logger.info(MosaicConstants.TAG, 'xBlocks: ' + xBlocks.toString() + ' ,yBlocks:' + yBlocks.toString());
// 不足一块的,按一块计算
if (xBlocks < 1) {
xBlocks = 1;
offMaxX = offMinX + blockSize;
}
if (yBlocks < 1) {
yBlocks = 1;
offMaxY = offMinY + blockSize;
}
// 遍历每个块
for (let y = 0; y < yBlocks; y++) {
for (let x = 0; x < xBlocks; x++) {
const startX = x * blockSize + offMinX;
const startY = y * blockSize + offMinY;
// 计算块内的平均颜色
let totalR = 0;
let totalG = 0;
let totalB = 0;
let pixelCount = 0;
for (let iy = startY; iy < startY + blockSize && iy < imageHeight && iy < offMaxY; iy++) {
for (let ix = startX; ix < startX + blockSize && ix < imageWidth && ix < offMaxX; ix++) {
// TODO 知识点:像素点数据包括RGB通道的分量值及图片透明度
const index = (iy * imageWidth + ix) * 4; // 4 像素点数据包括RGB通道的分量值及图片透明度
totalR += dataArray[index];
totalG += dataArray[index + 1];
totalB += dataArray[index + 2];
pixelCount++;
}
}
const averageR = Math.floor(totalR / pixelCount);
const averageG = Math.floor(totalG / pixelCount);
const averageB = Math.floor(totalB / pixelCount);
// TODO 知识点: 将块内平均颜色应用到块内的每个像素
for (let iy = startY; iy < startY + blockSize && iy < imageHeight && iy < offMaxY; iy++) {
for (let ix = startX; ix < startX + blockSize && ix < imageWidth && ix < offMaxX; ix++) {
const index = (iy * imageWidth + ix) * 4; // 4 像素点数据包括RGB通道的分量值及图片透明度
dataArray[index] = averageR;
dataArray[index + 1] = averageG;
dataArray[index + 2] = averageB;
}
}
}
}
return dataArray;
} catch (error) {
logger.error(MosaicConstants.TAG, 'applyMosaic fail,err:' + error);
return undefined;
}
}
总是有很多小伙伴反馈说:鸿蒙开发不知道学习哪些技术?不知道需要重点掌握哪些鸿蒙开发知识点? 为了解决大家这些学习烦恼。在这准备了一份很实用的鸿蒙全栈开发学习路线与学习文档给大家用来跟着学习。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植……等)技术知识点。
《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN/733GH/overview
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
鸿蒙开发面试真题(含参考答案):https://gitcode.com/HarmonyOS_MN/733GH/overview
OpenHarmony 开发环境搭建
《OpenHarmony源码解析》:https://gitcode.com/HarmonyOS_MN/733GH/overview
- 搭建开发环境
- Windows 开发环境的搭建
- Ubuntu 开发环境搭建
- Linux 与 Windows 之间的文件共享
- ……
- 系统架构分析
- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……