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_TextBlobCreateFromText构造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    // 如果需要多次测量,建议fontCollection作为全局变量使用,可以显著减少内存占用
388    OH_Drawing_FontCollection* fontCollection = OH_Drawing_CreateSharedFontCollection();
389    // 注册自定义字体
390    const char* fontFamily = "myFamilyName"; // myFamilyName为自定义字体的family name
391    const char* fontPath = "/data/storage/el2/base/haps/entry/files/myFontFile.ttf"; // 设置自定义字体所在的沙箱路径
392    OH_Drawing_RegisterFont(fontCollection, fontFamily, fontPath);
393    // 设置系统字体类型
394    const char* systemFontFamilies[] = {"Roboto"};
395    OH_Drawing_SetTextStyleFontFamilies(txtStyle, 1, systemFontFamilies);
396    OH_Drawing_SetTextStyleFontStyle(txtStyle, FONT_STYLE_NORMAL);
397    OH_Drawing_SetTextStyleLocale(txtStyle, "en");
398    // 设置自定义字体类型
399    auto txtStyle2 = OH_Drawing_CreateTextStyle();
400    OH_Drawing_SetTextStyleFontSize(txtStyle2, fontSize);
401    const char* myFontFamilies[] = {"myFamilyName"}; //如果已经注册自定义字体,填入自定义字体的family name使用自定义字体
402    OH_Drawing_SetTextStyleFontFamilies(txtStyle2, 1, myFontFamilies);
403    ```
404
4054. **生成最终文本显示效果**。
406
407    ```c++
408    OH_Drawing_TypographyCreate* handler = OH_Drawing_CreateTypographyHandler(typoStyle,
409        fontCollection);
410    OH_Drawing_TypographyHandlerPushTextStyle(handler, txtStyle);
411    OH_Drawing_TypographyHandlerPushTextStyle(handler, txtStyle2);
412    // 设置文字内容
413    const char* text = "Hello World Drawing\n";
414    OH_Drawing_TypographyHandlerAddText(handler, text);
415    OH_Drawing_TypographyHandlerPopTextStyle(handler);
416    OH_Drawing_Typography* typography = OH_Drawing_CreateTypography(handler);
417    // 设置页面最大宽度
418    double maxWidth = width_;
419    OH_Drawing_TypographyLayout(typography, maxWidth);
420    // 设置文本在画布上绘制的起始位置
421    double position[2] = {width_ / 5.0, height_ / 2.0};
422    // 将文本绘制到画布上
423    OH_Drawing_TypographyPaint(typography, cCanvas_, position[0], position[1]);
424    ```
4255. **释放变量**。
426
427    ```c++
428    // 确认typography不再使用时销毁
429    OH_Drawing_DestroyTypography(typography);
430    // 确认已经完成layout时销毁
431    OH_Drawing_DestroyTypographyHandler(handler);
432    // 确认不需要使用字体绘制功能时销毁
433    OH_Drawing_DestroyFontCollection(fontCollection);
434    // 确认已经完成layout时销毁
435    OH_Drawing_DestroyTextStyle(txtStyle);
436    OH_Drawing_DestroyTextStyle(txtStyle2);
437    // 确认已经完成layout时销毁
438    OH_Drawing_DestroyTypographyStyle(typoStyle);
439    ```
440
441#### 使用非定制排版能力实现文本绘制
442
4431. **创建画布和bitmap实例**。
444
445    ```c++
446    // 创建bitmap
447    cBitmap_ = OH_Drawing_BitmapCreate();
448    OH_Drawing_BitmapFormat cFormat {COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_OPAQUE};
449    // width的值必须为bufferHandle->stride / 4
450    OH_Drawing_BitmapBuild(cBitmap_, width_, height_, &cFormat);
451    // 创建canvas
452    cCanvas_ = OH_Drawing_CanvasCreate();
453    OH_Drawing_CanvasBind(cCanvas_, cBitmap_);
454    OH_Drawing_CanvasClear(cCanvas_, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF));
455    ```
456
4572. **面向应用具备自排版能力的文本绘制场景**。
458
459    ```c++
460    // 创建字体,并设置文字大小
461    OH_Drawing_Font* font = OH_Drawing_FontCreate();
462    OH_Drawing_FontSetTextSize(font, 40);
463    // 创建文本构造器
464    OH_Drawing_TextBlobBuilder* builder = OH_Drawing_TextBlobBuilderCreate();
465    // 申请一块内存
466    const OH_Drawing_RunBuffer* runBuffer = OH_Drawing_TextBlobBuilderAllocRunPos(builder, font, count, nullptr);
467    // glyphs、posX和posY是开发者自排版产生的数据,使用该数据填充内存
468    for (int idx = 0; idx < count; idx++) {
469        runBuffer->glyphs[idx] = glyphs[idx];
470        runBuffer->pos[idx * 2] = posX[idx];
471        runBuffer->pos[idx * 2 + 1] = posY[idx];
472    }
473    // 通过文本构造器创建文本
474    OH_Drawing_TextBlob* textBlob = OH_Drawing_TextBlobBuilderMake(builder);
475    // 释放内存
476    OH_Drawing_TextBlobBuilderDestroy(builder);
477    ```
478
4793. **面向应用使用默认排版能力的文本绘制场景**。
480
481    ```c++
482    // 创建字体,并设置文字大小
483    OH_Drawing_Font* font = OH_Drawing_FontCreate();
484    OH_Drawing_FontSetTextSize(font, 40);
485    // 创建要显示的字符
486    size_t size = 19;
487    const char16_t* buffer = u"Hello World Drawing";
488    // 通过字符和对应的编码格式创建默认排版的文本
489    OH_Drawing_TextBlob* textBlob = OH_Drawing_TextBlobCreateFromText(buffer, size * sizeof(char16_t), font, OH_Drawing_TextEncoding::TEXT_ENCODING_UTF16);
490    ```
491
4924. **设置画笔和画刷样式**。
493
494    ```c++
495    // 创建一个画刷Brush对象,Brush对象用于形状的填充
496    cBrush_ = OH_Drawing_BrushCreate();
497    OH_Drawing_BrushSetColor(cBrush_, OH_Drawing_ColorSetArgb(0xFF, 0x00, 0x00, 0x00));
498
499    // 将Brush画刷设置到canvas中
500    OH_Drawing_CanvasAttachBrush(cCanvas_, cBrush_);
501    ```
502
5035. **生成最终文本显示效果**。
504
505    ```c++
506    // 设置文本在画布上绘制的起始位置
507    double position[2] = {width_ / 5.0, height_ / 2.0};
508    // 将文本绘制到画布上
509    OH_Drawing_CanvasDrawTextBlob(canvas_, textBlob, position[0], position[1]);
510    // 释放内存
511    OH_Drawing_TextBlobDestroy(textBlob);
512    OH_Drawing_FontDestroy(font);
513    ```
514
515### 绘制内容送显
516
517前面我们已经通过Drawing API实现了Path绘制以及文字绘制。现在需要将其呈现在Native Window上。
518
5191. 通过前面``OnSurfaceCreatedCB``回调保存的Native Window指针,来申请Native Window Buffer。
520    ```c++
521    // 通过 OH_NativeWindow_NativeWindowRequestBuffer 获取 OHNativeWindowBuffer 实例
522    int32_t ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow_, &buffer_, &fenceFd_);
523    ```
5242. 通过``OH_NativeWindow_GetBufferHandleFromNative``获取bufferHandle。
525    ```c++
526    bufferHandle_ = OH_NativeWindow_GetBufferHandleFromNative(buffer_);
527    ```
5283. 使用系统mmap接口拿到bufferHandle的内存虚拟地址。
529    ```c++
530    mappedAddr_ = static_cast<uint32_t *>(
531        // 使用内存映射函数mmap将bufferHandle对应的共享内存映射到用户空间,可以通过映射出来的虚拟地址向bufferHandle中写入图像数据
532        // bufferHandle->virAddr是bufferHandle在共享内存中的起始地址,bufferHandle->size是bufferHandle在共享内存中的内存占用大小
533        mmap(bufferHandle_->virAddr, bufferHandle_->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle_->fd, 0));
534    if (mappedAddr_ == MAP_FAILED) {
535        DRAWING_LOGE("mmap failed");
536    }
537    ```
5384. 使用drawing_bitmap.h的``OH_Drawing_BitmapGetPixels``接口获取到画布绑定bitmap实例的像素地址,该地址指向的内存包含画布刚刚绘制的像素数据。将绘制内容填充到申请的Native Window Buffer中。
539    ```c++
540    // 画完后获取像素地址,地址指向的内存包含画布画的像素数据
541    void *bitmapAddr = OH_Drawing_BitmapGetPixels(cBitmap_);
542    uint32_t *value = static_cast<uint32_t *>(bitmapAddr);
543
544    // 使用mmap获取到的地址来访问内存
545    uint32_t *pixel = static_cast<uint32_t *>(mappedAddr_);
546    for (uint32_t x = 0; x < width_; x++) {
547        for (uint32_t y = 0; y < height_; y++) {
548            *pixel++ = *value++;
549        }
550    }
551    ```
5525. 设置刷新区域,并将其送显。
553    ```c++
554    // 如果Region中的Rect为nullptr,或者rectNumber为0,则认为OHNativeWindowBuffer全部有内容更改。
555    Region region {nullptr, 0};
556    // 通过OH_NativeWindow_NativeWindowFlushBuffer 提交给消费者使用,例如:显示在屏幕上。
557    OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow_, buffer_, fenceFd_, region);
558    ```
5596. 内存释放。
560
561    Drawing内存释放。
562
563    ```c++
564    // 去掉内存映射
565    int result = munmap(mappedAddr_, bufferHandle_->size);
566    if (result == -1) {
567        DRAWING_LOGE("munmap failed!");
568    }
569    // 销毁创建的对象
570    OH_Drawing_BrushDestroy(cBrush_);
571    cBrush_ = nullptr;
572    OH_Drawing_PenDestroy(cPen_);
573    cPen_ = nullptr;
574    OH_Drawing_PathDestroy(cPath_);
575    cPath_ = nullptr;
576    OH_Drawing_CanvasDestroy(cCanvas_);
577    cCanvas_ = nullptr;
578    OH_Drawing_BitmapDestroy(cBitmap_);
579    cBitmap_ = nullptr;
580    ```
581    Surface内存释放。
582
583    ```c++
584    void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) {
585        DRAWING_LOGI("OnSurfaceDestroyedCB");
586        if ((component == nullptr) || (window == nullptr)) {
587            DRAWING_LOGE("OnSurfaceDestroyedCB: component or window is null");
588            return;
589        }
590        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
591        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
592        if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
593            DRAWING_LOGE("OnSurfaceDestroyedCB: Unable to get XComponent id");
594            return;
595        }
596        std::string id(idStr);
597        SampleBitMap::Release(id);
598    }
599    ```
600### 用户调用
601
602以上为Native层C++代码,用户想要调用还需要通过ArkTS层代码对接。
6031. 定义ArkTS接口文件,命名XComponentContext.ts,用来对接Native代码。
604    ```ts
605    export default interface XComponentContext {
606      drawPattern(): void;
607      drawText(): void;
608    };
609    ```
610    在SampleBitMap类中添加初始化函数以及代码。
611    ```c++
612    void SampleBitMap::Export(napi_env env, napi_value exports) {
613        if ((env == nullptr) || (exports == nullptr)) {
614            DRAWING_LOGE("Export: env or exports is null");
615            return;
616        }
617        napi_property_descriptor desc[] = {
618            {"drawPattern", nullptr, SampleBitMap::NapiDrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
619            {"drawText", nullptr, SampleBitMap::NapiDrawText, nullptr, nullptr, nullptr, napi_default, nullptr}};
620        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
621        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
622            DRAWING_LOGE("Export: napi_define_properties failed");
623        }
624    }
625    ```
6262. 添加button控件供用户点击,并调用已定义的接口。
627    ```ts
628    build() {
629      Column() {
630        Row() {
631          XComponent({ id: 'xcomponentId', type: 'surface', libraryname: 'entry' })
632            .onLoad((xComponentContext) => {
633              this.xComponentContext = xComponentContext as XComponentContext;
634            }).width('640px') // Multiples of 64
635          }.height('88%')
636          Row() {
637            Button('Draw Path')
638              .fontSize('16fp')
639              .fontWeight(500)
640              .margin({ bottom: 24, right: 12 })
641              .onClick(() => {
642                console.log(TAG, "Draw Path click");
643                if (this.xComponentContext) {
644                  console.log(TAG, "Draw Path");
645                  this.xComponentContext.drawPattern();
646                  }
647              })
648              .width('33.6%')
649              .height(40)
650              .shadow(ShadowStyle.OUTER_DEFAULT_LG)
651            Button('Draw Text')
652              .fontSize('16fp')
653              .fontWeight(500)
654              .margin({ bottom: 24, left: 12 })
655              .onClick(() => {
656                  console.log(TAG, "draw text click");
657                  if (this.xComponentContext) {
658                    console.log(TAG, "draw text");
659                    this.xComponentContext.drawText();
660                  }
661              })
662              .width('33.6%')
663              .height(40)
664              .shadow(ShadowStyle.OUTER_DEFAULT_LG)
665          }
666          .width('100%')
667          .justifyContent(FlexAlign.Center)
668          .shadow(ShadowStyle.OUTER_DEFAULT_SM)
669          .alignItems(VerticalAlign.Bottom)
670          .layoutWeight(1)
671        }
672    }
673    ```
6743. 绘制与显示的效果图如下:
675
676    | 主页                                 | 绘制五角星                                         | 绘制文字                                            |
677    | ------------------------------------ |-----------------------------------------------| --------------------------------------------------- |
678    | ![main](./figures/drawIndex.jpg) | ![Draw Path](./figures/drawPath.jpg) | ![Draw Text](./figures/drawText.jpg) |
679
680
681##  相关实例
682
683为Drawing绘制关键代码,完整代码和更多针对Drawing的开放适用,请参考以下相关实例:
684
685- [Native Drawing(API10)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-5.0.2-Release/code/BasicFeature/Native/NdkDrawing)
686
687- [Native RenderNode Drawing(API12)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-5.0.2-Release/code/BasicFeature/Native/NdkRenderNodeDrawing)
688
689<!--RP1--><!--RP1End-->
690