• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用Drawing实现图形绘制与显示 (C/C++)
2
3## 场景介绍
4
5Native Drawing模块提供了一系列的接口用于基本图形和字体的绘制。
6
7Drawing绘制的内容无法直接在屏幕上显示,需要借用XComponent以及Native Window的能力支持,将绘制的内容通过Native Window送显。
8
9## 接口说明
10
11Drawing常用接口如下表所示,详细的接口说明请参考[Drawing](../reference/apis-arkgraphics2d/_drawing.md)。
12
13| 接口名 | 描述 |
14| -------- | -------- |
15| OH_Drawing_BitmapCreate (void) | 创建一个位图对象。 |
16| OH_Drawing_BitmapBuild (OH_Drawing_Bitmap *, const uint32_t width, const uint32_t height, const OH_Drawing_BitmapFormat *) | 初始化位图对象的宽度和高度,并且为该位图设置像素格式。 |
17| OH_Drawing_CanvasCreate (void) | 创建一个画布对象。 |
18| OH_Drawing_CanvasBind (OH_Drawing_Canvas *, OH_Drawing_Bitmap *) | 将一个位图对象绑定到画布中,使得画布绘制的内容输出到位图中(即CPU渲染)。 |
19| OH_Drawing_CanvasAttachBrush (OH_Drawing_Canvas *, const OH_Drawing_Brush *) | 设置画刷给画布,画布将会使用设置的画刷样式和颜色去填充绘制的图形形状。 |
20| OH_Drawing_CanvasAttachPen (OH_Drawing_Canvas *, const OH_Drawing_Pen *) | 设置画笔给画布,画布将会使用设置画笔的样式和颜色去绘制图形形状的轮廓。 |
21| OH_Drawing_CanvasDrawPath (OH_Drawing_Canvas *, const OH_Drawing_Path *) | 画一个自定义路径。 |
22| OH_Drawing_PathCreate (void) | 创建一个路径对象。 |
23| OH_Drawing_PathMoveTo (OH_Drawing_Path *, float x, float y) | 设置自定义路径的起始点位置。 |
24| OH_Drawing_PathLineTo (OH_Drawing_Path *, float x, float y) | 添加一条到目标点的线段。 |
25| OH_Drawing_PathClose (OH_Drawing_Path *) | 闭合路径,会添加一条到路径起点位置的线段。 |
26| OH_Drawing_PenCreate (void) | 创建一个画笔对象。 |
27| OH_Drawing_PenSetAntiAlias (OH_Drawing_Pen *, bool) | 设置抗锯齿属性,如果为真则说明画笔会启用抗锯齿功能,在绘制图形时会对图形的边缘像素进行半透明的模糊处理。 |
28| OH_Drawing_PenSetWidth (OH_Drawing_Pen *, float width) | 设置画笔的厚度属性,厚度属性描述了画笔绘制图形轮廓的宽度。 |
29| OH_Drawing_BrushCreate (void) | 创建一个画刷对象。 |
30| OH_Drawing_BrushSetColor (OH_Drawing_Brush *, uint32_t color) | 设置画刷的颜色属性,颜色属性描述了画刷填充图形时使用的颜色,用一个32位(ARGB)的变量表示。 |
31| OH_Drawing_CreateTypographyStyle (void) | 创建一个排版对象,用于定义排版样式。 |
32| OH_Drawing_CreateTextStyle (void) | 创建一个文本对象,用于定义文本样式。 |
33| OH_Drawing_TypographyHandlerAddText (OH_Drawing_TypographyCreate *, const char *) | 设置文本内容。 |
34| OH_Drawing_TypographyPaint (OH_Drawing_Typography *, OH_Drawing_Canvas *, double, double) | 显示文本。 |
35
36## 图形绘制与显示开发步骤
37
38### 开发流程
39
40使用Drawing进行图形绘制与显示时,需要使用Native Drawing模块的画布画笔绘制一个基本的2D图形;并将图形内容写入Native Window提供的图形Buffer,将Buffer提交到图形队列;再利用XComponent将C++代码层与ArkTS层对接,实现在ArkTS层调用绘制和显示的逻辑,最终在应用上显示图形。
41
42本文以实现2D图形和文本的绘制与显示为例,给出具体的开发指导。
43### 添加开发依赖
44
45**添加动态链接库**
46
47CMakeLists.txt中添加以下lib。
48
49```txt
50libace_napi.z.so
51libace_ndk.z.so
52libnative_window.so
53libnative_drawing.so
54```
55
56**头文件**
57```c++
58#include <ace/xcomponent/native_interface_xcomponent.h>
59#include "napi/native_api.h"
60#include <native_window/external_window.h>
61#include <native_drawing/drawing_bitmap.h>
62#include <native_drawing/drawing_color.h>
63#include <native_drawing/drawing_canvas.h>
64#include <native_drawing/drawing_pen.h>
65#include <native_drawing/drawing_brush.h>
66#include <native_drawing/drawing_path.h>
67#include <cmath>
68#include <algorithm>
69#include <stdint.h>
70#include <sys/mman.h>
71```
72
73### 使用XComponent构建绘制环境
74
751. 在Index.ets文件中添加XComponent组件。
76    ```ts
77    import XComponentContext from "../interface/XComponentContext";
78
79    const TAG = '[Sample_DrawingAPI]';
80
81    @Entry
82    @Component
83    struct Index {
84      private xComponentContext: XComponentContext | undefined = undefined;
85
86      build() {
87          Column() {
88          Row() {
89              XComponent({ id: 'xcomponentId', type: 'surface', libraryname: 'entry' })
90              .onLoad((xComponentContext) => {
91                  this.xComponentContext = xComponentContext as XComponentContext;
92              }).width('640px') // 64的倍数
93          }.height('88%')
94        }
95      }
96    }
97    ```
98    若要改变XComponent的宽,值需为64的倍数,例如640px。
992. 在 Native C++层获取NativeXComponent。建议使用单例模式保存XComponent。此步骤需要在napi_init的过程中处理。
100
101    创建一个PluginManger单例类,用于管理NativeXComponent。
102    ```c++
103    class PluginManager {
104    public:
105        ~PluginManager();
106
107        static PluginManager *GetInstance();
108
109        void SetNativeXComponent(std::string &id, OH_NativeXComponent *nativeXComponent);
110        SampleBitMap *GetRender(std::string &id);
111        void Export(napi_env env, napi_value exports);
112    private:
113
114        std::unordered_map<std::string, OH_NativeXComponent *> nativeXComponentMap_;
115        std::unordered_map<std::string, SampleBitMap *> pluginRenderMap_;
116    };
117    ```
118    SampleBitMap类会在后面的绘制2D图形步骤中创建。
119    ```c++
120    void PluginManager::Export(napi_env env, napi_value exports) {
121        if ((env == nullptr) || (exports == nullptr)) {
122            DRAWING_LOGE("Export: env or exports is null");
123            return;
124        }
125
126        napi_value exportInstance = nullptr;
127        if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
128            DRAWING_LOGE("Export: napi_get_named_property fail");
129            return;
130        }
131
132        OH_NativeXComponent *nativeXComponent = nullptr;
133        if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent)) != napi_ok) {
134            DRAWING_LOGE("Export: napi_unwrap fail");
135            return;
136        }
137
138        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
139        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
140        if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
141            DRAWING_LOGE("Export: OH_NativeXComponent_GetXComponentId fail");
142            return;
143        }
144
145        std::string id(idStr);
146        auto context = PluginManager::GetInstance();
147        if ((context != nullptr) && (nativeXComponent != nullptr)) {
148            context->SetNativeXComponent(id, nativeXComponent);
149            auto render = context->GetRender(id);
150            if (render != nullptr) {
151                render->RegisterCallback(nativeXComponent);
152                render->Export(env, exports);
153            } else {
154                DRAWING_LOGE("render is nullptr");
155            }
156        }
157    }
158    ```
1593. 注册回调函数。通过``OnSurfaceCreated``回调函数获取Native Window,建议将Native Window同样存储在单例中。
160    ```c++
161    // 定义回调函数
162    void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window)
163    {
164        // 可获取 OHNativeWindow 实例
165        OHNativeWindow* nativeWindow = static_cast<OHNativeWindow*>(window);
166        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
167        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
168        if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
169            DRAWING_LOGE("OnSurfaceCreatedCB: Unable to get XComponent id");
170            return;
171        }
172        std::string id(idStr);
173        auto render = SampleBitMap::GetInstance(id);
174        render->SetNativeWindow(nativeWindow);
175
176        uint64_t width;
177        uint64_t height;
178        int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
179        if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) {
180            render->SetHeight(height);
181            render->SetWidth(width);
182            DRAWING_LOGI("xComponent width = %{public}llu, height = %{public}llu", width, height);
183        }
184    }
185    void OnSurfaceChangedCB(OH_NativeXComponent* component, void* window)
186    {
187        // 可获取 OHNativeWindow 实例
188        OHNativeWindow* nativeWindow = static_cast<OHNativeWindow*>(window);
189        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
190        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
191        if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
192            DRAWING_LOGE("OnSurfaceChangedCB: Unable to get XComponent id");
193            return;
194        }
195        std::string id(idStr);
196        auto render = SampleBitMap::GetInstance(id);
197
198        uint64_t width;
199        uint64_t height;
200        int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
201        if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) {
202            render->SetHeight(height);
203            render->SetWidth(width);
204            DRAWING_LOGI("Surface Changed : xComponent width = %{public}llu, height = %{public}llu", width, height);
205        }
206    }
207    void OnSurfaceDestroyedCB(OH_NativeXComponent* component, void* window)
208    {
209        // 可获取 OHNativeWindow 实例
210        OHNativeWindow* nativeWindow = static_cast<OHNativeWindow*>(window);
211        // ...
212    }
213    void DispatchTouchEventCB(OH_NativeXComponent* component, void* window)
214    {
215        // 可获取 OHNativeWindow 实例
216        OHNativeWindow* nativeWindow = static_cast<OHNativeWindow*>(window);
217        // ...
218    }
219    ```
220    XComponent的所有Callback必须初始化,可以将不需要的Callback定义为空指针。
221    ```c++
222    // OH_NativeXComponent_Callback是个struct
223    OH_NativeXComponent_Callback callback;
224    callback.OnSurfaceCreated = OnSurfaceCreatedCB;
225    callback.OnSurfaceChanged = OnSurfaceChangedCB;
226    callback.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
227    callback.DispatchTouchEvent = DispatchTouchEventCB;
228    ```
2294. 将``OH_NativeXComponent_Callback``注册给NativeXComponent。
230    ```c++
231    // 注册回调函数
232    OH_NativeXComponent_RegisterCallback(nativeXComponent, &callback);
233    ```
234
235经过以上步骤,绘制环境已搭建完成,接下来介绍如何使用Drawing接口进行内容绘制。
236
237### 绘制2D图形
238
239以下步骤描述了如何使用Native Drawing模块的画布画笔绘制一个基本的2D图形:
240
2411. **创建Bitmap实例**。使用drawing_bitmap.h的``OH_Drawing_BitmapCreate``接口创建一个Bitmap实例cBitmap并使用``OH_Drawing_BitmapBuild``指定其长宽大小和像素格式。
242
243    创建一个SampleBitMap类,并声明接下来需要的私有成员变量。
244    ```c++
245    class SampleBitMap {
246    public:
247        // member functions
248    private:
249        OH_NativeXComponent_Callback renderCallback_;
250
251        uint64_t width_ = 0;
252        uint64_t height_ = 0;
253        OH_Drawing_Bitmap *cBitmap_ = nullptr;
254        OH_Drawing_Canvas *cCanvas_ = nullptr;
255        OH_Drawing_Path *cPath_ = nullptr;
256        OH_Drawing_Brush *cBrush_ = nullptr;
257        OH_Drawing_Pen *cPen_ = nullptr;
258        OHNativeWindow *nativeWindow_ = nullptr;
259        uint32_t *mappedAddr_ = nullptr;
260        BufferHandle *bufferHandle_ = nullptr;
261        struct NativeWindowBuffer *buffer_ = nullptr;
262        int fenceFd_ = 0;
263    };
264    ```
265
266    ```c++
267    // 创建一个bitmap对象
268    cBitmap_ = OH_Drawing_BitmapCreate();
269    // 定义bitmap的像素格式
270    OH_Drawing_BitmapFormat cFormat {COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_OPAQUE};
271    // 构造对应格式的bitmap,width的值必须为 bufferHandle->stride / 4
272    OH_Drawing_BitmapBuild(cBitmap_, width_, height_, &cFormat);
273    ```
274
2752. **创建画布实例**。使用drawing_canvas.h的 ``OH_Drawing_CanvasCreate`` 接口创建一个画布实例cCanvas,并使用 ``OH_Drawing_CanvasBind`` 接口将cBitmap实例绑定到cCanvas上,后续在画布上绘制的内容会输出到绑定的cBitmap实例中。
276
277    ```c++
278    // 创建一个canvas对象
279    cCanvas_ = OH_Drawing_CanvasCreate();
280    // 将画布与bitmap绑定,画布画的内容会输出到绑定的bitmap内存中
281    OH_Drawing_CanvasBind(cCanvas_, cBitmap_);
282    // 使用白色清除画布内容
283    OH_Drawing_CanvasClear(cCanvas_, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF));
284    ```
285
2863. **构造Path形状**。使用drawing_path.h提供的接口完成一个五角星形状的构造cPath。
287
288    ```c++
289    int len = height_ / 4;
290    float aX = width_ / 2;
291    float aY = height_ / 4;
292    float dX = aX - len * std::sin(18.0f);
293    float dY = aY + len * std::cos(18.0f);
294    float cX = aX + len * std::sin(18.0f);
295    float cY = dY;
296    float bX = aX + (len / 2.0);
297    float bY = aY + std::sqrt((cX - dX) * (cX - dX) + (len / 2.0) * (len / 2.0));
298    float eX = aX - (len / 2.0);
299    float eY = bY;
300
301    // 创建一个path对象,然后使用接口连接成一个五角星形状
302    cPath_ = OH_Drawing_PathCreate();
303    // 指定path的起始位置
304    OH_Drawing_PathMoveTo(cPath_, aX, aY);
305    // 用直线连接到目标点
306    OH_Drawing_PathLineTo(cPath_, bX, bY);
307    OH_Drawing_PathLineTo(cPath_, cX, cY);
308    OH_Drawing_PathLineTo(cPath_, dX, dY);
309    OH_Drawing_PathLineTo(cPath_, eX, eY);
310    // 闭合形状,path绘制完毕
311    OH_Drawing_PathClose(cPath_);
312    ```
313
3144. **设置画笔和画刷样式**。使用drawing_pen.h的``OH_Drawing_PenCreate``接口创建一个画笔实例cPen, 并设置抗锯齿、颜色、线宽等属性,画笔用于形状边框线的绘制。使用drawing_brush.h的``OH_Drawing_BrushCreate``接口创建一个画刷实例cBrush,并设置填充颜色, 画刷用于形状内部的填充。使用drawing_canvas.h的``OH_Drawing_CanvasAttachPen``和``OH_Drawing_CanvasAttachBrush``接口将画笔画刷的实例设置到画布实例中。
315
316    ```c++
317    // 创建一个画笔Pen对象,Pen对象用于形状的边框线绘制
318    cPen_ = OH_Drawing_PenCreate();
319    OH_Drawing_PenSetAntiAlias(cPen_, true);
320    OH_Drawing_PenSetColor(cPen_, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0x00, 0x00));
321    OH_Drawing_PenSetWidth(cPen_, 10.0);
322    OH_Drawing_PenSetJoin(cPen_, LINE_ROUND_JOIN);
323    // 将Pen画笔设置到canvas中
324    OH_Drawing_CanvasAttachPen(cCanvas_, cPen_);
325
326    // 创建一个画刷Brush对象,Brush对象用于形状的填充
327    cBrush_ = OH_Drawing_BrushCreate();
328    OH_Drawing_BrushSetColor(cBrush_, OH_Drawing_ColorSetArgb(0xFF, 0x00, 0xFF, 0x00));
329
330    // 将Brush画刷设置到canvas中
331    OH_Drawing_CanvasAttachBrush(cCanvas_, cBrush_);
332    ```
333
3345. **绘制Path形状**。使用drawing_canvas.h的``OH_Drawing_CanvasDrawPath``接口将五角星绘制到画布上。
335
336    ```c++
337    // 在画布上画path的形状,五角星的边框样式为pen设置,颜色填充为Brush设置
338    OH_Drawing_CanvasDrawPath(cCanvas_, cPath_);
339    ```
340
341### 文本绘制开发步骤
342
343Native Drawing模块关于文本绘制提供两类API接口:
344
345- 一类是具有定制排版能力的接口:如OH_Drawing_Typography,OH_Drawing_TypographyStyle,OH_Drawing_TextStyle等类型。支撑用户设置排版风格和文本风格,可调用OH_Drawing_TypographyHandlerAddText添加文本并调用OH_Drawing_TypographyLayout和OH_Drawing_TypographyPaint对文本进行排版和绘制。
346- 另一类是不具有定制排版能力的接口:如OH_Drawing_Font,OH_Drawing_TextBlob,OH_Drawing_RunBuffer等类型。支撑具备自排版能力的用户将排版结果构造为OH_Drawing_TextBlob并调用OH_Drawing_CanvasDrawTextBlob绘制OH_Drawing_TextBlob描述的文本块。
347
348以下分别提供了如何使用这两类API接口以实现文本绘制的具体步骤。
349
350#### 使用定制排版能力实现文本绘制
351
3521. **创建画布和bitmap实例**。
353
354    ```c++
355    // 创建bitmap
356    cBitmap_ = OH_Drawing_BitmapCreate();
357    OH_Drawing_BitmapFormat cFormat {COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_OPAQUE};
358    // width的值必须为bufferHandle->stride / 4
359    OH_Drawing_BitmapBuild(cBitmap_, width_, height_, &cFormat);
360    // 创建canvas
361    cCanvas_ = OH_Drawing_CanvasCreate();
362    OH_Drawing_CanvasBind(cCanvas_, cBitmap_);
363    OH_Drawing_CanvasClear(cCanvas_, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF));
364    ```
365
3662. **设置排版风格**。
367
368    ```c++
369    // 选择从左到右/左对齐等排版属性
370    OH_Drawing_TypographyStyle* typoStyle = OH_Drawing_CreateTypographyStyle();
371    OH_Drawing_SetTypographyTextDirection(typoStyle, TEXT_DIRECTION_LTR);
372    OH_Drawing_SetTypographyTextAlign(typoStyle, TEXT_ALIGN_LEFT);
373    ```
374
3753. **设置文本风格**。
376
377    ```c++
378    // 设置文字颜色,例如黑色
379    OH_Drawing_TextStyle* txtStyle = OH_Drawing_CreateTextStyle();
380    OH_Drawing_SetTextStyleColor(txtStyle, OH_Drawing_ColorSetArgb(0xFF, 0x00, 0x00, 0x00));
381    // 设置文字大小、字重等属性
382    double fontSize = width_ / 15;
383    OH_Drawing_SetTextStyleFontSize(txtStyle, fontSize);
384    OH_Drawing_SetTextStyleFontWeight(txtStyle, FONT_WEIGHT_400);
385    OH_Drawing_SetTextStyleBaseLine(txtStyle, TEXT_BASELINE_ALPHABETIC);
386    OH_Drawing_SetTextStyleFontHeight(txtStyle, 1);
387    OH_Drawing_FontCollection* fontCollection = OH_Drawing_CreateFontCollection();
388    // 注册自定义字体
389    const char* fontFamily = "myFamilyName"; // myFamilyName为自定义字体的family name
390    const char* fontPath = "/data/storage/el2/base/haps/entry/files/myFontFile.ttf"; // 设置自定义字体所在的沙箱路径
391    OH_Drawing_RegisterFont(fontCollection, fontFamily, fontPath);
392    // 设置系统字体类型
393    const char* systemFontFamilies[] = {"Roboto"};
394    OH_Drawing_SetTextStyleFontFamilies(txtStyle, 1, systemFontFamilies);
395    OH_Drawing_SetTextStyleFontStyle(txtStyle, FONT_STYLE_NORMAL);
396    OH_Drawing_SetTextStyleLocale(txtStyle, "en");
397    // 设置自定义字体类型
398    auto txtStyle2 = OH_Drawing_CreateTextStyle();
399    OH_Drawing_SetTextStyleFontSize(txtStyle2, fontSize);
400    const char* myFontFamilies[] = {"myFamilyName"}; //如果已经注册自定义字体,填入自定义字体的family name使用自定义字体
401    OH_Drawing_SetTextStyleFontFamilies(txtStyle2, 1, myFontFamilies);
402    ```
403
4044. **生成最终文本显示效果**。
405
406    ```c++
407    OH_Drawing_TypographyCreate* handler = OH_Drawing_CreateTypographyHandler(typoStyle,
408        fontCollection);
409    OH_Drawing_TypographyHandlerPushTextStyle(handler, txtStyle);
410    OH_Drawing_TypographyHandlerPushTextStyle(handler, txtStyle2);
411    // 设置文字内容
412    const char* text = "Hello World Drawing\n";
413    OH_Drawing_TypographyHandlerAddText(handler, text);
414    OH_Drawing_TypographyHandlerPopTextStyle(handler);
415    OH_Drawing_Typography* typography = OH_Drawing_CreateTypography(handler);
416    // 设置页面最大宽度
417    double maxWidth = width_;
418    OH_Drawing_TypographyLayout(typography, maxWidth);
419    // 设置文本在画布上绘制的起始位置
420    double position[2] = {width_ / 5.0, height_ / 2.0};
421    // 将文本绘制到画布上
422    OH_Drawing_TypographyPaint(typography, cCanvas_, position[0], position[1]);
423    ```
4245. **释放变量**。
425
426    ```c++
427    OH_Drawing_DestroyTypography(typography);
428    OH_Drawing_DestroyTypographyHandler(handler);
429    OH_Drawing_DestroyFontCollection(fontCollection);
430    OH_Drawing_DestroyTextStyle(txtStyle);
431    OH_Drawing_DestroyTextStyle(txtStyle2);
432    OH_Drawing_DestroyTypographyStyle(typoStyle);
433    ```
434
435#### 使用非定制排版能力实现文本绘制
436
4371. **创建画布和bitmap实例**。
438
439    ```c++
440    // 创建bitmap
441    cBitmap_ = OH_Drawing_BitmapCreate();
442    OH_Drawing_BitmapFormat cFormat {COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_OPAQUE};
443    // width的值必须为bufferHandle->stride / 4
444    OH_Drawing_BitmapBuild(cBitmap_, width_, height_, &cFormat);
445    // 创建canvas
446    cCanvas_ = OH_Drawing_CanvasCreate();
447    OH_Drawing_CanvasBind(cCanvas_, cBitmap_);
448    OH_Drawing_CanvasClear(cCanvas_, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF));
449    ```
450
4512. **应用具备自排版能力的文本绘制**。
452
453    ```c++
454    // 创建字体,并设置文字大小
455    OH_Drawing_Font* font = OH_Drawing_FontCreate();
456    OH_Drawing_FontSetTextSize(font, 40);
457    // 创建文本构造器
458    OH_Drawing_TextBlobBuilder* builder = OH_Drawing_TextBlobBuilderCreate();
459    // 申请一块内存
460    const OH_Drawing_RunBuffer* runBuffer = OH_Drawing_TextBlobBuilderAllocRunPos(builder, font, count, nullptr);
461    // glyphs、posX和posY是开发者自排版产生的数据,使用该数据填充内存
462    for (int idx = 0; idx < count; idx++) {
463        runBuffer->glyphs[idx] = glyphs[idx];
464        runBuffer->pos[idx * 2] = posX[idx];
465        runBuffer->pos[idx * 2 + 1] = posY[idx];
466    }
467    // 通过文本构造器创建文本
468    OH_Drawing_TextBlob* textBlob = OH_Drawing_TextBlobBuilderMake(builder);
469    // 释放内存
470    OH_Drawing_TextBlobBuilderDestroy(builder);
471    ```
472
4733. **设置画笔和画刷样式**。
474
475    ```c++
476    // 创建一个画刷Brush对象,Brush对象用于形状的填充
477    cBrush_ = OH_Drawing_BrushCreate();
478    OH_Drawing_BrushSetColor(cBrush_, OH_Drawing_ColorSetArgb(0xFF, 0x00, 0x00, 0x00));
479
480    // 将Brush画刷设置到canvas中
481    OH_Drawing_CanvasAttachBrush(cCanvas_, cBrush_);
482    ```
483
4844. **生成最终文本显示效果**。
485
486    ```c++
487    // 设置文本在画布上绘制的起始位置
488    double position[2] = {width_ / 5.0, height_ / 2.0};
489    // 将文本绘制到画布上
490    OH_Drawing_CanvasDrawTextBlob(canvas_, textBlob, position[0], position[1]);
491    // 释放内存
492    OH_Drawing_TextBlobDestroy(textBlob);
493    OH_Drawing_FontDestroy(font);
494    ```
495
496### 绘制内容送显
497
498前面我们已经通过Drawing API实现了Path绘制以及文字绘制。现在需要将其呈现在Native Window上。
499
5001. 通过前面``OnSurfaceCreatedCB``回调保存的Native Window指针,来申请Native Window Buffer。
501    ```c++
502    // 通过 OH_NativeWindow_NativeWindowRequestBuffer 获取 OHNativeWindowBuffer 实例
503    int32_t ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow_, &buffer_, &fenceFd_);
504    ```
5052. 通过``OH_NativeWindow_GetBufferHandleFromNative``获取bufferHandle。
506    ```c++
507    bufferHandle_ = OH_NativeWindow_GetBufferHandleFromNative(buffer_);
508    ```
5093. 使用系统mmap接口拿到bufferHandle的内存虚拟地址。
510    ```c++
511    mappedAddr_ = static_cast<uint32_t *>(
512        // 使用内存映射函数mmap将bufferHandle对应的共享内存映射到用户空间,可以通过映射出来的虚拟地址向bufferHandle中写入图像数据
513        // bufferHandle->virAddr是bufferHandle在共享内存中的起始地址,bufferHandle->size是bufferHandle在共享内存中的内存占用大小
514        mmap(bufferHandle_->virAddr, bufferHandle_->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle_->fd, 0));
515    if (mappedAddr_ == MAP_FAILED) {
516        DRAWING_LOGE("mmap failed");
517    }
518    ```
5194. 使用drawing_bitmap.h的``OH_Drawing_BitmapGetPixels``接口获取到画布绑定bitmap实例的像素地址,该地址指向的内存包含画布刚刚绘制的像素数据。将绘制内容填充到申请的Native Window Buffer中。
520    ```c++
521    // 画完后获取像素地址,地址指向的内存包含画布画的像素数据
522    void *bitmapAddr = OH_Drawing_BitmapGetPixels(cBitmap_);
523    uint32_t *value = static_cast<uint32_t *>(bitmapAddr);
524
525    // 使用mmap获取到的地址来访问内存
526    uint32_t *pixel = static_cast<uint32_t *>(mappedAddr_);
527    for (uint32_t x = 0; x < width_; x++) {
528        for (uint32_t y = 0; y < height_; y++) {
529            *pixel++ = *value++;
530        }
531    }
532    ```
5335. 设置刷新区域,并将其送显。
534    ```c++
535    // 如果Region中的Rect为nullptr,或者rectNumber为0,则认为OHNativeWindowBuffer全部有内容更改。
536    Region region {nullptr, 0};
537    // 通过OH_NativeWindow_NativeWindowFlushBuffer 提交给消费者使用,例如:显示在屏幕上。
538    OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow_, buffer_, fenceFd_, region);
539    ```
5406. 内存释放。
541
542    Drawing内存释放。
543
544    ```c++
545    // 去掉内存映射
546    int result = munmap(mappedAddr_, bufferHandle_->size);
547    if (result == -1) {
548        DRAWING_LOGE("munmap failed!");
549    }
550    // 销毁创建的对象
551    OH_Drawing_BrushDestroy(cBrush_);
552    cBrush_ = nullptr;
553    OH_Drawing_PenDestroy(cPen_);
554    cPen_ = nullptr;
555    OH_Drawing_PathDestroy(cPath_);
556    cPath_ = nullptr;
557    OH_Drawing_CanvasDestroy(cCanvas_);
558    cCanvas_ = nullptr;
559    OH_Drawing_BitmapDestroy(cBitmap_);
560    cBitmap_ = nullptr;
561    ```
562    Surface内存释放。
563
564    ```c++
565    void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) {
566        DRAWING_LOGI("OnSurfaceDestroyedCB");
567        if ((component == nullptr) || (window == nullptr)) {
568            DRAWING_LOGE("OnSurfaceDestroyedCB: component or window is null");
569            return;
570        }
571        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
572        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
573        if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
574            DRAWING_LOGE("OnSurfaceDestroyedCB: Unable to get XComponent id");
575            return;
576        }
577        std::string id(idStr);
578        SampleBitMap::Release(id);
579    }
580    ```
581### 用户调用
582
583以上为Native层C++代码,用户想要调用还需要通过ArkTS层代码对接。
5841. 定义ArkTS接口文件,命名XComponentContext.ts,用来对接Native代码。
585    ```ts
586    export default interface XComponentContext {
587      drawPattern(): void;
588      drawText(): void;
589    };
590    ```
591    在SampleBitMap类中添加初始化函数以及代码。
592    ```c++
593    void SampleBitMap::Export(napi_env env, napi_value exports) {
594        if ((env == nullptr) || (exports == nullptr)) {
595            DRAWING_LOGE("Export: env or exports is null");
596            return;
597        }
598        napi_property_descriptor desc[] = {
599            {"drawPattern", nullptr, SampleBitMap::NapiDrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
600            {"drawText", nullptr, SampleBitMap::NapiDrawText, nullptr, nullptr, nullptr, napi_default, nullptr}};
601        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
602        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
603            DRAWING_LOGE("Export: napi_define_properties failed");
604        }
605    }
606    ```
6072. 添加button控件供用户点击,并调用已定义的接口。
608    ```ts
609    build() {
610      Column() {
611        Row() {
612          XComponent({ id: 'xcomponentId', type: 'surface', libraryname: 'entry' })
613            .onLoad((xComponentContext) => {
614              this.xComponentContext = xComponentContext as XComponentContext;
615            }).width('640px') // Multiples of 64
616          }.height('88%')
617          Row() {
618            Button('Draw Path')
619              .fontSize('16fp')
620              .fontWeight(500)
621              .margin({ bottom: 24, right: 12 })
622              .onClick(() => {
623                console.log(TAG, "Draw Path click");
624                if (this.xComponentContext) {
625                  console.log(TAG, "Draw Path");
626                  this.xComponentContext.drawPattern();
627                  }
628              })
629              .width('33.6%')
630              .height(40)
631              .shadow(ShadowStyle.OUTER_DEFAULT_LG)
632            Button('Draw Text')
633              .fontSize('16fp')
634              .fontWeight(500)
635              .margin({ bottom: 24, left: 12 })
636              .onClick(() => {
637                  console.log(TAG, "draw text click");
638                  if (this.xComponentContext) {
639                    console.log(TAG, "draw text");
640                    this.xComponentContext.drawText();
641                  }
642              })
643              .width('33.6%')
644              .height(40)
645              .shadow(ShadowStyle.OUTER_DEFAULT_LG)
646          }
647          .width('100%')
648          .justifyContent(FlexAlign.Center)
649          .shadow(ShadowStyle.OUTER_DEFAULT_SM)
650          .alignItems(VerticalAlign.Bottom)
651          .layoutWeight(1)
652        }
653    }
654    ```
6553. 绘制与显示的效果图如下:
656
657    | 主页                                 | 绘制五角星                                         | 绘制文字                                            |
658    | ------------------------------------ |-----------------------------------------------| --------------------------------------------------- |
659    | ![main](./figures/drawIndex.jpg) | ![Draw Path](./figures/drawPath.jpg) | ![Draw Text](./figures/drawText.jpg) |
660
661##  相关实例
662以上代码为Drawing绘制关键代码,完整代码请参考下面sample。
663
664- [Native Drawing(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NdkDrawing)
665