# RT-Thread GUI 繪圖引擎-硬體
Rtgui 中的 dc(drawable canvas) 也就是繪圖引擎,可以說是 rtgui 中最重要的一個部分,其中分成 3 個部分:給硬體的 dc_hw、給 buffer 的 dc_buffer 以及給 client 的。
接下來將會追蹤 dc_hw 中的程式碼,分析 rtgui 是如何在螢幕上面描繪點線面。
---
## 結構
:::success
**File:** dc.h
:::
### dc
```c=63
/*
* The abstract device context
*
* Normally, a DC is a drawable canvas, user can draw point/line/cycle etc
* on the DC.
*
* There are several kinds of DC:
* - Hardware DC;
* - Client DC;
* - Buffer DC;
*/
struct rtgui_dc
{
/* type of device context */
rt_uint32_t type;
/* dc engine */
const struct rtgui_dc_engine *engine;
};
```
---
### dc_engine
```c=49
struct rtgui_dc_engine
{
/* interface */
void (*draw_point)(struct rtgui_dc *dc, int x, int y);
void (*draw_color_point)(struct rtgui_dc *dc, int x, int y, rtgui_color_t color);
void (*draw_vline)(struct rtgui_dc *dc, int x, int y1, int y2);
void (*draw_hline)(struct rtgui_dc *dc, int x1, int x2, int y);
void (*fill_rect)(struct rtgui_dc *dc, rtgui_rect_t *rect);
void (*blit_line)(struct rtgui_dc *dc, int x1, int x2, int y, rt_uint8_t *line_data);
void (*blit)(struct rtgui_dc *dc, struct rtgui_point *dc_point, struct rtgui_dc *dest, rtgui_rect_t *rect);
rt_bool_t (*fini)(struct rtgui_dc *dc);
};
```
---
### dc_hw
```c=83
* The hardware device context
*
* The hardware DC is a context based on hardware device, for examle the
* LCD device. The operations on the hardware DC are reflected to the real
* hardware.
*
*/
struct rtgui_dc_hw
{
struct rtgui_dc parent;
rtgui_widget_t *owner;
const struct rtgui_graphic_driver *hw_driver;
};
```
---
### hw_engine
:::success
**File:** dc_hw.c
:::
```c=44
const struct rtgui_dc_engine dc_hw_engine =
{
rtgui_dc_hw_draw_point,
rtgui_dc_hw_draw_color_point,
rtgui_dc_hw_draw_vline,
rtgui_dc_hw_draw_hline,
rtgui_dc_hw_fill_rect,
rtgui_dc_hw_blit_line,
rtgui_dc_hw_blit,
rtgui_dc_hw_fini,
};
```
---
## 啟動 dc
我們可以從 rtgui 官方提供的範例發現,在使用 dc 前,需要先利用 `rtgui_dc_begin_drawing` 來啟動引擎,並在結束時呼叫 `rtgui_dc_end_drawing`;而啟動時,會判斷要使用哪種 dc,並啟動,如 1866 至 1871 行
```c=1866
/* create client or hardware DC */
if ((rtgui_region_is_flat(&owner->clip) == RT_EOK) &&
rtgui_rect_is_equal(&(owner->extent), &(owner->clip.extents)) == RT_EOK)
dc = rtgui_dc_hw_create(owner);
else
dc = rtgui_dc_client_create(owner);
```
如果判斷為 hw,則進入 `rtgui_dc_hw_create`
---
## 建立 dc
<i class="fa fa-code"></i> Code: `rtgui_dc_hw_create`
| 功能 | 回傳值 |
| --- | ------ |
| 建立 dc | dc 指標 |
| `*owner` |
| -------- |
| dc 擁有者 |
```c=57
struct rtgui_dc *rtgui_dc_hw_create(rtgui_widget_t *owner)
{
struct rtgui_dc_hw *dc;
/* adjudge owner */
if (owner == RT_NULL || owner->toplevel == RT_NULL) return RT_NULL;
/* create DC */
dc = (struct rtgui_dc_hw *) rtgui_malloc(sizeof(struct rtgui_dc_hw));
if (dc)
{
dc->parent.type = RTGUI_DC_HW;
dc->parent.engine = &dc_hw_engine;
dc->owner = owner;
dc->hw_driver = rtgui_graphic_driver_get_default();
return &(dc->parent);
}
return RT_NULL;
}
```
---
## 運作 dc (畫圖)
### 點
#### `rtgui_dc_hw_draw_point`
| 功能 | 回傳值 |
| --- | ------ |
| 畫點 | void |
| `*self` | `x` | `y` |
| ------- | :-: | :-: |
| dc 本體 | 座標 x | 座標 y |
```c=89
/*
* draw a logic point on device
*/
static void rtgui_dc_hw_draw_point(struct rtgui_dc *self, int x, int y)
{
struct rtgui_dc_hw *dc;
RT_ASSERT(self != RT_NULL);
dc = (struct rtgui_dc_hw *) self;
if (x < 0 || y < 0)
return;
x = x + dc->owner->extent.x1;
if (x >= dc->owner->extent.x2)
return;
y = y + dc->owner->extent.y1;
if (y >= dc->owner->extent.y2)
return;
/* draw this point */
dc->hw_driver->ops->set_pixel(&(dc->owner->gc.foreground), x, y);
}
```
首先傳進去的座標一律為邏輯位置,也就是以此 dc 所屬物件(有可能是視窗、元件等)的 $(x_1,y_1)$ 為原點之座標;由於 $(x_1,y_1)$ 為該物件(通常為矩形)的左下角,所以傳入的座標不會有負號。
接著將邏輯座標轉為實際座標(也就是螢幕上的真正位置),所以把 $(x,y)$ 轉成 $(x+x_1,y+y_1)$;由於 dc 是跟隨物件的,所以新座標不可超過 $(x_2,y_2)$,也就是右上角。
最後利用驅動中設定好的 `set_pixel` 函數來上色,這裡使用預設顏色。
---
### 彩色點
#### `rtgui_dc_hw_draw_color_point`
| 功能 | 回傳值 |
| --- | ------ |
| 畫彩色點 | void |
| `*self` | `x` | `y` | `color` |
| ------- | :-: | :-: | ------- |
| dc 本體 | 座標 x | 座標 y | 所選的顏色 |
```c=113
static void rtgui_dc_hw_draw_color_point(struct rtgui_dc *self, int x, int y, rtgui_color_t color)
{
struct rtgui_dc_hw *dc;
RT_ASSERT(self != RT_NULL);
dc = (struct rtgui_dc_hw *) self;
if (x < 0 || y < 0)
return;
x = x + dc->owner->extent.x1;
if (x >= dc->owner->extent.x2)
return;
y = y + dc->owner->extent.y1;
if (y >= dc->owner->extent.y2)
return;
/* draw this point */
dc->hw_driver->ops->set_pixel(&color, x, y);
}
```
跟上面最大的不同是可以選顏色 (131)。
---
### 水平線
```c=134
/*
* draw a logic vertical line on device
*/
static void rtgui_dc_hw_draw_vline(struct rtgui_dc *self, int x, int y1, int y2)
{
struct rtgui_dc_hw *dc;
RT_ASSERT(self != RT_NULL);
dc = (struct rtgui_dc_hw *) self;
if (x < 0)
return;
x = x + dc->owner->extent.x1;
if (x >= dc->owner->extent.x2)
return;
y1 = y1 + dc->owner->extent.y1;
y2 = y2 + dc->owner->extent.y1;
if (y1 > y2)
_int_swap(y1, y2);
if (y1 > dc->owner->extent.y2 || y2 < dc->owner->extent.y1)
return;
if (y1 < dc->owner->extent.y1)
y1 = dc->owner->extent.y1;
if (y2 > dc->owner->extent.y2)
y2 = dc->owner->extent.y2;
/* draw vline */
dc->hw_driver->ops->draw_vline(&(dc->owner->gc.foreground), x, y1, y2);
}
```
---
### 鉛直線
```c=167
/*
* draw a logic horizontal line on device
*/
static void rtgui_dc_hw_draw_hline(struct rtgui_dc *self, int x1, int x2, int y)
{
struct rtgui_dc_hw *dc;
RT_ASSERT(self != RT_NULL);
dc = (struct rtgui_dc_hw *) self;
if (y < 0)
return;
y = y + dc->owner->extent.y1;
if (y >= dc->owner->extent.y2)
return;
/* convert logic to device */
x1 = x1 + dc->owner->extent.x1;
x2 = x2 + dc->owner->extent.x1;
if (x1 > x2)
_int_swap(x1, x2);
if (x1 > dc->owner->extent.x2 || x2 < dc->owner->extent.x1)
return;
if (x1 < dc->owner->extent.x1)
x1 = dc->owner->extent.x1;
if (x2 > dc->owner->extent.x2)
x2 = dc->owner->extent.x2;
/* draw hline */
dc->hw_driver->ops->draw_hline(&(dc->owner->gc.foreground), x1, x2, y);
}
```
---
### 矩形
```c=200
static void rtgui_dc_hw_fill_rect(struct rtgui_dc *self, struct rtgui_rect *rect)
{
rtgui_color_t color;
register rt_base_t y1, y2, x1, x2;
struct rtgui_dc_hw *dc;
RT_ASSERT(self != RT_NULL);
RT_ASSERT(rect);
dc = (struct rtgui_dc_hw *) self;
/* get background color */
color = dc->owner->gc.background;
/* convert logic to device */
x1 = rect->x1 + dc->owner->extent.x1;
if (x1 > dc->owner->extent.x2)
return;
if (x1 < dc->owner->extent.x1)
x1 = dc->owner->extent.x1;
x2 = rect->x2 + dc->owner->extent.x1;
if (x2 < dc->owner->extent.x1)
return;
if (x2 > dc->owner->extent.x2)
x2 = dc->owner->extent.x2;
y1 = rect->y1 + dc->owner->extent.y1;
if (y1 > dc->owner->extent.y2)
return;
if (y1 < dc->owner->extent.y1)
y1 = dc->owner->extent.y1;
y2 = rect->y2 + dc->owner->extent.y1;
if (y2 < dc->owner->extent.y1)
return;
if (y2 > dc->owner->extent.y2)
y2 = dc->owner->extent.y2;
/* fill rect */
for (; y1 < y2; y1++)
{
dc->hw_driver->ops->draw_hline(&color, x1, x2, y1);
}
}
```
---
### blit(?)
```c=243
static void rtgui_dc_hw_blit_line(struct rtgui_dc *self, int x1, int x2, int y, rt_uint8_t *line_data)
{
struct rtgui_dc_hw *dc;
RT_ASSERT(self != RT_NULL);
dc = (struct rtgui_dc_hw *) self;
/* convert logic to device */
if (y < 0)
return;
y = y + dc->owner->extent.y1;
if (y > dc->owner->extent.y2)
return;
x1 = x1 + dc->owner->extent.x1;
x2 = x2 + dc->owner->extent.x1;
if (x1 > x2)
_int_swap(x1, x2);
if (x1 > dc->owner->extent.x2 || x2 < dc->owner->extent.x1)
return;
if (x1 < dc->owner->extent.x1)
x1 = dc->owner->extent.x1;
if (x2 > dc->owner->extent.x2)
x2 = dc->owner->extent.x2;
dc->hw_driver->ops->draw_raw_hline(line_data, x1, x2, y);
}
```
---
```c=272
static void rtgui_dc_hw_blit(struct rtgui_dc *dc,
struct rtgui_point *dc_point,
struct rtgui_dc *dest,
rtgui_rect_t *rect)
{
/* not blit in hardware dc */
return ;
}
```
###### tags: `GUI` `RT-Thread` `DC`