---
# System prepended metadata

title: RT-Thread GUI 繪圖引擎-硬體
tags: [GUI, DC, RT-Thread]

---

# 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`