• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 画布的获取与绘制结果的显示(C/C++)
2
3<!--Kit: ArkGraphics 2D-->
4<!--Subsystem: Graphics-->
5<!--Owner: @hangmengxin-->
6<!--Designer: @wangyanglan-->
7<!--Tester: @nobuggers-->
8<!--Adviser: @ge-yafang-->
9
10## 场景介绍
11
12Canvas即画布,提供绘制基本图形的能力,用于在屏幕上绘制图形和处理图形。开发者可以通过Canvas实现自定义的绘图效果,增强应用的用户体验。
13
14Canvas是图形绘制的核心,本章中提到的所有绘制操作(包括基本图形的绘制、文字的绘制、图片的绘制、图形变换等)都是基于Canvas的。
15
16目前C/C++有两种获取Canvas的方式:获取可直接上屏显示的Canvas、获取离屏的Canvas,前者在调用绘制接口之后无需进行额外的操作即可完成绘制结果的上屏显示,而后者需要依靠已有的显示手段来显示绘制结果。
17
18
19## 接口说明
20
21创建Canvas常用接口如下表所示,详细的使用和参数说明请见[drawing_canvas.h](../reference/apis-arkgraphics2d/capi-drawing-canvas-h.md)。
22
23| 接口 | 描述 |
24| -------- | -------- |
25| OH_Drawing_Canvas\* OH_Drawing_CanvasCreate (void) | 用于创建一个画布对象。 |
26| void OH_Drawing_CanvasBind (OH_Drawing_Canvas\*, OH_Drawing_Bitmap\*) | 用于将一个位图对象绑定到画布中,使得画布绘制的内容输出到位图中。 |
27| OH_Drawing_Canvas\* OH_Drawing_SurfaceGetCanvas (OH_Drawing_Surface \*) | 通过surface对象获取画布对象。 |
28
29
30## 获取可直接显示的Canvas画布
31
32通过XComponent获取可直接显示的Canvas画布。
33
341. 添加链接库。
35
36   在Native工程的src/main/cpp/CMakeLists.txt,添加如下链接库:
37
38   ```c++
39   target_link_libraries(entry PUBLIC libnative_drawing.so)
40   ```
41
422. 导入依赖的相关头文件。
43
44   ```c++
45   #include <native_drawing/drawing_canvas.h>
46   #include <native_drawing/drawing_surface.h>
47   ```
48
493. 从XComponent对应的NativeWindow中获取BufferHandle对象。NativeWindow相关的API请参考[_native_window](../reference/apis-arkgraphics2d/capi-nativewindow.md)。
50
51   ```c++
52   uint64_t width, height;
53   OHNativeWindow *nativeWindow;    // NativeWindow及其宽高需要从XComponent获取
54   int32_t usage = NATIVEBUFFER_USAGE_CPU_READ | NATIVEBUFFER_USAGE_CPU_WRITE | NATIVEBUFFER_USAGE_MEM_DMA;
55   int ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow, SET_USAGE, usage);
56   if (ret != 0) {
57       return;
58   }
59
60   struct NativeWindowBuffer *buffer = nullptr;
61   int fenceFd = 0;
62   ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow, &buffer, &fenceFd);
63   if (ret != 0) {
64       return;
65   }
66
67   BufferHandle* bufferHandle = OH_NativeWindow_GetBufferHandleFromNative(buffer);
68   ```
69
704. 从BufferHandle中获取对应的内存地址。
71
72   ```c++
73   uint32_t* mappedAddr = static_cast<uint32_t *>(mmap(bufferHandle->virAddr, bufferHandle->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle->fd, 0));
74   ```
75
765. 创建窗口画布。
77
78   ```c++
79   OH_Drawing_Image_Info screenImageInfo = {static_cast<int32_t>(width), static_cast<int32_t>(height), COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_OPAQUE};
80   OH_Drawing_Bitmap* screenBitmap = OH_Drawing_BitmapCreateFromPixels(&screenImageInfo, mappedAddr, bufferHandle->stride);
81   OH_Drawing_Canvas* screenCanvas = OH_Drawing_CanvasCreate();
82   OH_Drawing_CanvasBind(screenCanvas, screenBitmap);
83   ```
84
856. 利用上一步中得到的Canvas进行自定义的绘制操作,即本章下文中的内容。
86
877. 利用XComponent完成显示。
88
89   ```c++
90   Region region {nullptr, 0};
91   OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, buffer, fenceFd, region);
92   ```
93
94
95## 离屏Canvas画布的获取与显示
96
97目前有两种创建离屏Canvas的方式:创建CPU后端Canvas、创建GPU后端Canvas,这两种Canvas都需要依靠XComponent来完成绘制结果的上屏显示。由于历史原因,早期的Canvas都是CPU后端Canvas。目前已经支持GPU后端Canvas,GPU的并行计算能力更强,更适合图形绘制。但GPU后端Canvas对部分场景的支持还有欠缺,比如复杂的路径,对于简短文字的绘制性能也比不上CPU后端Canvas。
98
99
100### CPU后端Canvas的创建与显示
101
102目前C/C++接口的绘制需要依赖于NativeWindow,CPU后端Canvas需要先离屏绘制,生成位图或像素图(从API Version 20开始支持),再借助XComponent上屏。
103
104
105方式一:通过绑定位图(Bitmap)的方式创建Canvas。
1061. 导入依赖的相关头文件。
107
108   ```c++
109   #include <native_drawing/drawing_canvas.h>
110   #include <native_drawing/drawing_bitmap.h>
111   ```
112
1132. 创建基于CPU的Canvas。需要通过OH_Drawing_BitmapCreate()接口创建一个位图对象(具体可参考[图片绘制](pixelmap-drawing-c.md)),并通过OH_Drawing_CanvasBind()接口将位图绑定到Canvas中,从而使得Canvas绘制的内容可以输出到位图中。
114
115   ```c++
116   // 创建一个位图对象
117   OH_Drawing_Bitmap* bitmap = OH_Drawing_BitmapCreate();
118   OH_Drawing_BitmapFormat cFormat{COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_PREMUL};
119   // 设置位图长宽(按需设置)
120   uint32_t width = 800;
121   uint32_t height = 800;
122   // 初始化位图
123   OH_Drawing_BitmapBuild(bitmap, width, height, &cFormat);
124   // 创建一个Canvas对象
125   OH_Drawing_Canvas* bitmapCanvas = OH_Drawing_CanvasCreate();
126   // 将Canvas与bitmap绑定,Canvas绘制的内容会输出到绑定的bitmap内存中
127   OH_Drawing_CanvasBind(bitmapCanvas, bitmap);
128   ```
129
130   如果需要将背景设置为白色,需要执行以下步骤:
131
132   ```c++
133   OH_Drawing_CanvasClear(bitmapCanvas, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF));
134   ```
135
1363. 将上一步中创建的位图绘制到[窗口画布](#获取可直接显示的canvas画布)上。
137
138   ```c++
139   OH_Drawing_CanvasDrawBitmap(screenCanvas, bitmap, 0, 0);
140   ```
141
142
143方式二:通过像素图(PixelMap)创建Canvas。从API Version 20开始,支持使用此种方式创建Canvas。
144像素图是系统中用来表示图片的统一的数据结构,相比于drawing模块中提供的位图,像素图具备通用性,并且能够更好地发挥系统的能力。
145
1461. 添加链接库。
147
148   在Native工程的src/main/cpp/CMakeLists.txt,添加如下链接库:
149
150   ```c++
151   target_link_libraries(entry PUBLIC libhilog_ndk.z.so libpixelmap.so)
152   ```
153
1542. 导入依赖的相关头文件。
155
156   ```c++
157   #include <multimedia/image_framework/image/pixelmap_native.h>
158   #include <native_drawing/drawing_pixel_map.h>
159   ```
160
1613. 需要通过OH_Drawing_PixelMapGetFromOhPixelMapNative()接口创建一个像素图对象(具体可参考[图片绘制](pixelmap-drawing-c.md)),并通过OH_Drawing_CanvasCreateWithPixelMap()接口借助像素图对象创建Canvas。
162
163   ```c++
164   // 图片宽高
165   uint32_t width = 600;
166   uint32_t height = 400;
167   // 设置位图格式(长、宽、颜色类型、透明度类型)
168   OH_Pixelmap_InitializationOptions *createOps = nullptr;
169   OH_PixelmapInitializationOptions_Create(&createOps);
170   OH_PixelmapInitializationOptions_SetWidth(createOps, width);
171   OH_PixelmapInitializationOptions_SetHeight(createOps, height);
172   OH_PixelmapInitializationOptions_SetPixelFormat(createOps, PIXEL_FORMAT_RGBA_8888);
173   OH_PixelmapInitializationOptions_SetAlphaType(createOps, PIXELMAP_ALPHA_TYPE_UNKNOWN);
174   // 字节长度,RGBA_8888每个像素占4字节
175   size_t bufferSize = width * height * 4;
176   void *buffer = malloc(bufferSize);
177   // 创建OH_PixelmapNative对象
178   OH_PixelmapNative *pixelMapNative = nullptr;
179   OH_PixelmapNative_CreatePixelmap(static_cast<uint8_t *>(buffer), bufferSize, createOps, &pixelMapNative);
180   // 创建Pixelmap对象
181   OH_Drawing_PixelMap *pixelMap = OH_Drawing_PixelMapGetFromOhPixelMapNative(pixelMapNative);
182   // 创建Canvas对象
183   OH_Drawing_Canvas* pixelmapCanvas = OH_Drawing_CanvasCreateWithPixelMap(pixelMap);
184   ```
185
186   如果需要将背景设置为白色,需要执行以下步骤:
187
188   ```c++
189   OH_Drawing_CanvasClear(pixelmapCanvas, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF));
190   ```
191
1924. 将上一步中创建的像素图绘制到[窗口画布](#获取可直接显示的canvas画布)上。
193
194   ```c++
195   // PixelMap中像素的截取区域
196   OH_Drawing_Rect *src = OH_Drawing_RectCreate(0, 0, width, height);
197   // 画布中显示的区域
198   OH_Drawing_Rect *dst = OH_Drawing_RectCreate(0, 0, width, height);
199   // 采样选项对象
200   OH_Drawing_SamplingOptions* samplingOptions = OH_Drawing_SamplingOptionsCreate(
201       OH_Drawing_FilterMode::FILTER_MODE_LINEAR, OH_Drawing_MipmapMode::MIPMAP_MODE_LINEAR);
202   // 绘制PixelMap
203   OH_Drawing_CanvasDrawPixelMapRect(screenCanvas, pixelMap, src, dst, samplingOptions);
204   ```
205
206
207### GPU后端Canvas的创建与显示
208
209GPU后端Canvas指画布是基于GPU进行绘制的,GPU的并行计算能力优于CPU,适用于绘制图片或区域相对大的场景,但目前GPU后端的Canvas针对绘制复杂路径的能力还有欠缺。同CPU后端Canvas,目前C/C++接口的绘制需要依赖于XComponent,GPU后端Canvas需要先离屏绘制再借助XComponent上屏。
210
2111. 当前创建GPU后端的Canvas依赖EGL的能力,需要在CMakeList.txt中添加EGL的动态依赖库。
212
213   ```c++
214   libEGL.so
215   ```
216
2172. 导入依赖的头文件。
218
219   ```c++
220   #include <EGL/egl.h>
221   #include <EGL/eglext.h>
222   #include <native_drawing/drawing_gpu_context.h>
223   #include <native_drawing/drawing_surface.h>
224   ```
2253. 初始化EGL上下文。
226
227   ```c++
228   // 初始化上下文相关参数
229   EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
230   EGLConfig mEGLConfig = nullptr;
231   EGLContext mEGLContext = EGL_NO_CONTEXT;
232   EGLSurface mEGLSurface = nullptr;
233   ```
234
235   ```c++
236   // 初始化上下文相关配置
237   EGLConfig getConfig(int version, EGLDisplay eglDisplay)
238   {
239      int attribList[] = {EGL_SURFACE_TYPE,
240                           EGL_WINDOW_BIT,
241                           EGL_RED_SIZE,
242                           8,
243                           EGL_GREEN_SIZE,
244                           8,
245                           EGL_BLUE_SIZE,
246                           8,
247                           EGL_ALPHA_SIZE,
248                           8,
249                           EGL_RENDERABLE_TYPE,
250                           EGL_OPENGL_ES2_BIT,
251                           EGL_NONE};
252      EGLConfig configs = NULL;
253      int configsNum;
254
255      if (!eglChooseConfig(eglDisplay, attribList, &configs, 1, &configsNum)) {
256         return NULL;
257      }
258
259      return configs;
260   }
261
262   // 在需要初始化EGL上下文处调用InitializeEglContext
263   int32_t InitializeEglContext(EGLDisplay mEGLDisplay, EGLConfig mEGLConfig,
264      EGLContext mEGLContext, EGLSurface mEGLSurface)
265   {
266      mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
267      if (mEGLDisplay == EGL_NO_DISPLAY) {
268         return -1;
269      }
270
271      EGLint eglMajVers;
272      EGLint eglMinVers;
273      if (!eglInitialize(mEGLDisplay, &eglMajVers, &eglMinVers)) {
274         mEGLDisplay = EGL_NO_DISPLAY;
275         return -1;
276      }
277
278      int version = 3;
279      mEGLConfig = getConfig(version, mEGLDisplay);
280      if (mEGLConfig == nullptr) {
281         return -1;
282      }
283
284      int attribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
285
286      mEGLContext = eglCreateContext(mEGLDisplay, mEGLConfig, EGL_NO_CONTEXT, attribList);
287
288      EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
289      mEGLSurface = eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, attribs);
290      if (!eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
291         return -1;
292      }
293
294      return 0;
295   }
296   ```
297
2984. 创建GPU后端Canvas。GPU后端Canvas需要借助Surface对象来获取,需先创建surface,surface的API请参考[drawing_surface.h](../reference/apis-arkgraphics2d/capi-drawing-surface-h.md)。通过OH_Drawing_GpuContextCreateFromGL接口创建绘图上下文,再将这个上下文作为参数创建surface,最后通过OH_Drawing_SurfaceGetCanvas接口从surface中获取到canvas。
299
300   ```c++
301   // 设置宽高(按需设定)
302   int32_t cWidth = 800;
303   int32_t cHeight = 800;
304   // 设置图像,包括宽度、高度、颜色格式和透明度格式
305   OH_Drawing_Image_Info imageInfo = {cWidth, cHeight, COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_PREMUL};
306   // GPU上下文的选项
307   OH_Drawing_GpuContextOptions options{false};
308   // 创建一个使用OpenGL(GL)作为其GPU后端的绘图上下文
309   OH_Drawing_GpuContext *gpuContext = OH_Drawing_GpuContextCreateFromGL(options);
310   // 创建surface对象
311   OH_Drawing_Surface *surface = OH_Drawing_SurfaceCreateFromGpuContext(gpuContext, true, imageInfo);
312   // 创建一个canvas对象
313   OH_Drawing_Canvas* gpuCanvas = OH_Drawing_SurfaceGetCanvas(surface);
314   ```
315
316   如果需要将背景设置为白色,需要执行以下步骤:
317
318   ```c++
319   OH_Drawing_CanvasClear(gpuCanvas, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF));
320   ```
321
3225. 将上一步中的绘制结果拷贝到[窗口画布](#获取可直接显示的canvas画布)上。
323
324   ```c++
325   void* dstPixels = malloc(cWidth * cHeight * 4); // 4 for rgba
326   OH_Drawing_CanvasReadPixels(gpuCanvas, &imageInfo, dstPixels, 4 * cWidth, 0, 0);
327   OH_Drawing_Bitmap* bitmap = OH_Drawing_BitmapCreateFromPixels(&imageInfo, dstPixels, 4 * cWidth);
328   OH_Drawing_CanvasDrawBitmap(screenCanvas, bitmap, 0, 0);
329   ```
330
3316. 使用完之后需要将EGL上下文销毁。
332
333   ```c++
334   // 在需要销毁处调用DeInitializeEglContext销毁EGL上下文。
335   void DeInitializeEglContext(EGLDisplay mEGLDisplay, EGLContext mEGLContext, EGLSurface mEGLSurface)
336   {
337      // 以下三个方法都有返回值判断是否销毁成功,必要时可进行调试。
338      eglDestroySurface(mEGLDisplay, mEGLSurface);
339      eglDestroyContext(mEGLDisplay, mEGLContext);
340      eglTerminate(mEGLDisplay);
341
342      mEGLSurface = NULL;
343      mEGLContext = NULL;
344      mEGLDisplay = NULL;
345   }
346   ```
347
348<!--RP1-->
349## 相关实例
350
351针对Drawing(C/C++)的开发,有以下相关实例可供参考:
352
353- [NDKGraphicsDraw (API14)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/DocsSample/Drawing/NDKGraphicsDraw)
354<!--RP1End-->