• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 画布的获取与绘制结果的显示(C/C++)
2
3
4## 场景介绍
5
6Canvas即画布,提供绘制基本图形的能力,用于在屏幕上绘制图形和处理图形。开发者可以通过Canvas实现自定义的绘图效果,增强应用的用户体验。
7
8Canvas是图形绘制的核心,本章中提到的所有绘制操作(包括基本图形的绘制、文字的绘制、图片的绘制、图形变换等)都是基于Canvas的。
9
10目前C/C++有两种获取Canvas的方式:获取可直接上屏显示的Canvas、获取离屏的Canvas,前者在调用绘制接口之后无需进行额外的操作即可完成绘制结果的上屏显示,而后者需要依靠已有的显示手段来显示绘制结果。
11
12
13## 接口说明
14
15创建Canvas常用接口如下表所示,详细的使用和参数说明请见[drawing_canvas.h](../reference/apis-arkgraphics2d/drawing__canvas_8h.md)。
16
17| 接口 | 描述 |
18| -------- | -------- |
19| OH_Drawing_Canvas\* OH_Drawing_CanvasCreate (void) | 用于创建一个画布对象。 |
20| void OH_Drawing_CanvasBind (OH_Drawing_Canvas\*, OH_Drawing_Bitmap\*) | 用于将一个位图对象绑定到画布中,使得画布绘制的内容输出到位图中。 |
21| OH_Drawing_Canvas\* OH_Drawing_SurfaceGetCanvas (OH_Drawing_Surface \*) | 通过surface对象获取画布对象。 |
22
23
24## 获取可直接显示的Canvas画布
25
26通过XComponent获取可直接显示的Canvas画布。
27
281. 从XComponent对应的NativeWindow中获取BufferHandle对象。NativeWindow相关的API请参考[_native_window](../reference/apis-arkgraphics2d/_native_window.md)。
29
30   ```c++
31   uint64_t width, height;
32   OHNativeWindow *nativeWindow;    // NativeWindow及其宽高需要从XComponent获取
33   int32_t usage = NATIVEBUFFER_USAGE_CPU_READ | NATIVEBUFFER_USAGE_CPU_WRITE | NATIVEBUFFER_USAGE_MEM_DMA;
34   int ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow, SET_USAGE, usage);
35   if (ret != 0) {
36       return;
37   }
38
39   struct NativeWindowBuffer *buffer = nullptr;
40   int fenceFd = 0;
41   ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow, &buffer, &fenceFd);
42   if (ret != 0) {
43       return;
44   }
45
46   BufferHandle* bufferHandle = OH_NativeWindow_GetBufferHandleFromNative(buffer);
47   ```
48
492. 从BufferHandle中获取对应的内存地址。
50
51   ```c++
52   uint32_t* mappedAddr = static_cast<uint32_t *>(mmap(bufferHandle->virAddr, bufferHandle->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle->fd, 0));
53   ```
54
553. 创建窗口画布。
56
57   ```c++
58   OH_Drawing_Image_Info screenImageInfo = {static_cast<int32_t>(width), static_cast<int32_t>(height), COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_OPAQUE};
59   OH_Drawing_Bitmap* screenBitmap = OH_Drawing_BitmapCreateFromPixels(&screenImageInfo, mappedAddr, bufferHandle->stride);
60   OH_Drawing_Canvas* screenCanvas = OH_Drawing_CanvasCreate();
61   OH_Drawing_CanvasBind(screenCanvas, screenBitmap);
62   ```
63
644. 利用上一步中得到的Canvas进行自定义的绘制操作,即本章下文中的内容。
65
665. 利用XComponent完成显示。
67
68   ```c++
69   Region region {nullptr, 0};
70   OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, buffer, fenceFd, region);
71   ```
72
73
74## 离屏Canvas画布的获取与显示
75
76目前有两种创建离屏Canvas的方式:创建CPU后端Canvas、创建GPU后端Canvas,这两种Canvas都需要依靠XComponent来完成绘制结果的上屏显示。由于历史原因,早期的Canvas都是CPU后端Canvas。目前已经支持GPU后端Canvas,GPU的并行计算能力更强,更适合图形绘制。但GPU后端Canvas对部分场景的支持还有欠缺,比如复杂的路径,对于简短文字的绘制性能也比不上CPU后端Canvas。
77
78
79### CPU后端Canvas的创建与显示
80
81目前C/C++接口的绘制需要依赖于NativeWindow,CPU后端Canvas需要先离屏绘制,生成位图(Bitmap),再借助XComponent将位图上屏。
82
83
841. 导入依赖的相关头文件。
85
86   ```c++
87   #include <native_drawing/drawing_canvas.h>
88   #include <native_drawing/drawing_bitmap.h>
89   ```
90
912. 创建基于CPU的Canvas。需要通过OH_Drawing_BitmapCreate()接口创建一个位图对象(具体可参考[图片绘制](pixelmap-drawing-c.md)),并通过OH_Drawing_CanvasBind()接口将位图绑定到Canvas中,从而使得Canvas绘制的内容可以输出到位图中。
92
93   ```c++
94   // 创建一个位图对象
95   OH_Drawing_Bitmap* bitmap = OH_Drawing_BitmapCreate();
96   OH_Drawing_BitmapFormat cFormat{COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_PREMUL};
97   // 设置位图长宽(按需设置)
98   uint32_t width = 800;
99   uint32_t height = 800;
100   // 初始化位图
101   OH_Drawing_BitmapBuild(bitmap, width, height, &cFormat);
102   // 创建一个Canvas对象
103   OH_Drawing_Canvas* bitmapCanvas = OH_Drawing_CanvasCreate();
104   // 将Canvas与bitmap绑定,Canvas绘制的内容会输出到绑定的bitmap内存中
105   OH_Drawing_CanvasBind(bitmapCanvas, bitmap);
106   ```
107
108   如果需要将背景设置为白色,需要执行以下步骤:
109
110   ```c++
111   OH_Drawing_CanvasClear(bitmapCanvas, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF));
112   ```
113
1143. 将上一步中创建的位图绘制到[窗口画布](#获取可直接显示的canvas画布)上。
115
116   ```c++
117   OH_Drawing_CanvasDrawBitmap(screenCanvas, bitmap, 0, 0);
118   ```
119
120
121### GPU后端Canvas的创建与显示
122
123GPU后端Canvas指画布是基于GPU进行绘制的,GPU的并行计算能力优于CPU,适用于绘制图片或区域相对大的场景,但目前GPU后端的Canvas针对绘制复杂路径的能力还有欠缺。同CPU后端Canvas,目前C/C++接口的绘制需要依赖于XComponent,GPU后端Canvas需要先离屏绘制再借助XComponent上屏。
124
1251. 当前创建GPU后端的Canvas依赖EGL的能力,需要在CMakeList.txt中添加EGL的动态依赖库。
126
127   ```c++
128   libEGL.so
129   ```
130
1312. 导入依赖的头文件。
132
133   ```c++
134   #include <EGL/egl.h>
135   #include <EGL/eglext.h>
136   #include <native_drawing/drawing_gpu_context.h>
137   #include <native_drawing/drawing_surface.h>
138   ```
1393. 初始化EGL上下文。
140
141   ```c++
142   // 初始化上下文相关参数
143   EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
144   EGLConfig mEGLConfig = nullptr;
145   EGLContext mEGLContext = EGL_NO_CONTEXT;
146   EGLSurface mEGLSurface = nullptr;
147   ```
148
149   ```c++
150   // 初始化上下文相关配置
151   EGLConfig getConfig(int version, EGLDisplay eglDisplay)
152   {
153      int attribList[] = {EGL_SURFACE_TYPE,
154                           EGL_WINDOW_BIT,
155                           EGL_RED_SIZE,
156                           8,
157                           EGL_GREEN_SIZE,
158                           8,
159                           EGL_BLUE_SIZE,
160                           8,
161                           EGL_ALPHA_SIZE,
162                           8,
163                           EGL_RENDERABLE_TYPE,
164                           EGL_OPENGL_ES2_BIT,
165                           EGL_NONE};
166      EGLConfig configs = NULL;
167      int configsNum;
168
169      if (!eglChooseConfig(eglDisplay, attribList, &configs, 1, &configsNum)) {
170         return NULL;
171      }
172
173      return configs;
174   }
175
176   // 在需要初始化EGL上下文处调用InitializeEglContext
177   int32_t InitializeEglContext(EGLDisplay mEGLDisplay, EGLConfig mEGLConfig,
178      EGLContext mEGLContext, EGLSurface mEGLSurface)
179   {
180      mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
181      if (mEGLDisplay == EGL_NO_DISPLAY) {
182         return -1;
183      }
184
185      EGLint eglMajVers;
186      EGLint eglMinVers;
187      if (!eglInitialize(mEGLDisplay, &eglMajVers, &eglMinVers)) {
188         mEGLDisplay = EGL_NO_DISPLAY;
189         return -1;
190      }
191
192      int version = 3;
193      mEGLConfig = getConfig(version, mEGLDisplay);
194      if (mEGLConfig == nullptr) {
195         return -1;
196      }
197
198      int attribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
199
200      mEGLContext = eglCreateContext(mEGLDisplay, mEGLConfig, EGL_NO_CONTEXT, attribList);
201
202      EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
203      mEGLSurface = eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, attribs);
204      if (!eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
205         return -1;
206      }
207
208      return 0;
209   }
210   ```
211
2124. 创建GPU后端Canvas。GPU后端Canvas需要借助Surface对象来获取,需先创建surface,surface的API请参考[drawing_surface.h](../reference/apis-arkgraphics2d/drawing__surface_8h.md)。通过OH_Drawing_GpuContextCreateFromGL接口创建绘图上下文,再将这个上下文作为参数创建surface,最后通过OH_Drawing_SurfaceGetCanvas接口从surface中获取到canvas。
213
214   ```c++
215   // 设置宽高(按需设定)
216   int32_t cWidth = 800;
217   int32_t cHeight = 800;
218   // 设置图像,包括宽度、高度、颜色格式和透明度格式
219   OH_Drawing_Image_Info imageInfo = {cWidth, cHeight, COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_PREMUL};
220   // GPU上下文的选项
221   OH_Drawing_GpuContextOptions options{false};
222   // 创建一个使用OpenGL(GL)作为其GPU后端的绘图上下文
223   OH_Drawing_GpuContext *gpuContext = OH_Drawing_GpuContextCreateFromGL(options);
224   // 创建surface对象
225   OH_Drawing_Surface *surface = OH_Drawing_SurfaceCreateFromGpuContext(gpuContext, true, imageInfo);
226   // 创建一个canvas对象
227   OH_Drawing_Canvas* gpuCanvas = OH_Drawing_SurfaceGetCanvas(surface);
228   ```
229
230   如果需要将背景设置为白色,需要执行以下步骤:
231
232   ```c++
233   OH_Drawing_CanvasClear(gpuCanvas, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF));
234   ```
235
2365. 将上一步中的绘制结果拷贝到[窗口画布](#获取可直接显示的canvas画布)上。
237
238   ```c++
239   void* dstPixels = malloc(cWidth * cHeight * 4); // 4 for rgba
240   OH_Drawing_CanvasReadPixels(gpuCanvas, &imageInfo, dstPixels, 4 * cWidth, 0, 0);
241   OH_Drawing_Bitmap* bitmap = OH_Drawing_BitmapCreateFromPixels(&imageInfo, dstPixels, 4 * cWidth);
242   OH_Drawing_CanvasDrawBitmap(screenCanvas, bitmap, 0, 0);
243   ```
244
2456. 使用完之后需要将EGL上下文销毁。
246
247   ```c++
248   // 在需要销毁处调用DeInitializeEglContext销毁EGL上下文。
249   void DeInitializeEglContext(EGLDisplay mEGLDisplay, EGLContext mEGLContext, EGLSurface mEGLSurface)
250   {
251      // 以下三个方法都有返回值判断是否销毁成功,必要时可进行调试。
252      eglDestroySurface(mEGLDisplay, mEGLSurface);
253      eglDestroyContext(mEGLDisplay, mEGLContext);
254      eglTerminate(mEGLDisplay);
255
256      mEGLSurface = NULL;
257      mEGLContext = NULL;
258      mEGLDisplay = NULL;
259   }
260   ```
261
262<!--RP1-->
263## 相关实例
264
265针对Drawing(C/C++)的开发,有以下相关实例可供参考:
266
267- [NDKGraphicsDraw (API14)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/DocsSample/Drawing/NDKGraphicsDraw)
268<!--RP1End-->
269