Newer
Older
lynxi-plugin / src / util / drawTool.cpp
#include "drawTool.h"
#include "lyn_plugin_dev.h"

#include "freetype/freetype.h"

#include <map>
#include <mutex>

#define CHECK_RETURN(cond, ret, log) if (cond) {/*LOG(ERROR) << log;*/ return ret;}

struct YUVColor {
    uint8_t colorY;
    uint8_t colorU;
    uint8_t colorV;
};

std::map<DrawColor, YUVColor> g_sampleColorMap = {
    {SAMPLE_DRAW_COLOR_WHITE,  {180, 128, 128}},
    {SAMPLE_DRAW_COLOR_BLACK,  {16, 128, 128}},
    {SAMPLE_DRAW_COLOR_RED,    {65, 100, 212}},
    {SAMPLE_DRAW_COLOR_GREEN,  {112, 72, 58}},
    {SAMPLE_DRAW_COLOR_BLUE,   {35, 212, 114}},
    {SAMPLE_DRAW_COLOR_YELLOW, {162, 44, 142}},
    {SAMPLE_DARW_COLOR_CYAN,   {131, 156, 44}}
};

int PluginDrawBox(DrawBoxAttr *attr)
{
    if (attr == nullptr || attr->imgData == nullptr) {
        return lynEPTR;
    }

    if (attr->startX > attr->imgW ||
        attr->startY > attr->imgH ||
        attr->boxW + attr->startX > attr->imgW ||
        attr->boxH + attr->startY > attr->imgH) {
        LOG_PLUGIN_W("box point not in range, imgW %u imgH %u startX %u startY %u boxW %u boxH %u\n",
            attr->imgW, attr->imgH, attr->startX, attr->startY, attr->boxW, attr->boxH);
        return lynEINVARG;
    }

    attr->startX = (attr->startX / 2) * 2;
    attr->startY = (attr->startY / 2) * 2;
    attr->boxW = (attr->boxW / 2) * 2;
    attr->boxH = (attr->boxH / 2) * 2;

    if ((int)attr->boxW < 2 * attr->thickness || (int)attr->boxH < 2 * attr->thickness) {
        LOG_PLUGIN_W("box size less than twice of thickness, boxW %u boxH %u thickness %u\n",
            attr->boxW, attr->boxH, attr->thickness);
        return lynEINVARG;
    }

    if (attr->imgFmt != LYN_PIX_FMT_YUV420P &&
        attr->imgFmt != LYN_PIX_FMT_NV12 &&
        attr->imgFmt != LYN_PIX_FMT_NV21) {
        LOG_PLUGIN_W("format %d not support", attr->imgFmt);
        return lynEFORMAT;
    }

    unsigned char * dataY = attr->imgData;
    unsigned char * dataU = attr->imgData + attr->imgW * attr->imgH;
    unsigned char * dataV = attr->imgData + (attr->imgW * attr->imgH) * 5 / 4;
    unsigned char colorY = g_sampleColorMap[attr->color].colorY;
    unsigned char colorU = g_sampleColorMap[attr->color].colorU;
    unsigned char colorV = g_sampleColorMap[attr->color].colorV;

    for (int i = 0; i < attr->thickness; i++) {
        memset(dataY + attr->imgW * (attr->startY + i) + attr->startX, colorY, attr->boxW);
        memset(dataY + attr->imgW * (attr->startY + attr->boxH - attr->thickness + i) + attr->startX, colorY, attr->boxW);
        if (i % 2 != 0) { // uv 分量消减
            continue;
        }

        if (attr->imgFmt == LYN_PIX_FMT_YUV420P) {
            memset(dataU + attr->imgW / 2 * ((attr->startY + i) / 2) + attr->startX / 2, colorU, attr->boxW / 2);
            memset(dataV + attr->imgW / 2 * ((attr->startY + i) / 2) + attr->startX / 2, colorV, attr->boxW / 2);
            memset(dataU + attr->imgW / 2 * ((attr->startY + attr->boxH - attr->thickness + i) / 2) + attr->startX / 2, colorU, attr->boxW / 2);
            memset(dataV + attr->imgW / 2 * ((attr->startY + attr->boxH - attr->thickness + i) / 2) + attr->startX / 2, colorV, attr->boxW / 2);
        } else if (attr->imgFmt == LYN_PIX_FMT_NV12) {
            unsigned char * colorTop = dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX;
            unsigned char * colorBottom = dataU + attr->imgW* ((attr->startY + attr->boxH - attr->thickness + i) / 2) + attr->startX;
            for (uint32_t j = 0; j < attr->boxW / 2; j++) {
                *(colorTop + 2 * j) = colorU;
                *(colorTop + 2 * j + 1) = colorV;
                *(colorBottom + 2 * j) = colorU;
                *(colorBottom + 2 * j + 1) = colorV;
            }
        } else if (attr->imgFmt == LYN_PIX_FMT_NV21) {
            unsigned char * colorTop = dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX;
            unsigned char * colorBottom = dataU + attr->imgW* ((attr->startY + attr->boxH - attr->thickness + i) / 2) + attr->startX;
            for (uint32_t j = 0; j < attr->boxW / 2; j++) {
                *(colorTop + 2 * j) = colorV;
                *(colorTop + 2 * j + 1) = colorU;
                *(colorBottom + 2 * j) = colorV;
                *(colorBottom + 2 * j + 1) = colorU;
            }
        }
    }

    for (int i = attr->thickness; i < (int)attr->boxH - attr->thickness; i++) {
        memset(dataY + attr->imgW * (attr->startY + i) + attr->startX, colorY, attr->thickness);
        memset(dataY + attr->imgW * (attr->startY + i) + attr->startX + attr->boxW - attr->thickness, colorY, attr->thickness);

        if (i % 2 != 0) {
            continue;
        }

        if (attr->imgFmt == LYN_PIX_FMT_YUV420P) {
            memset(dataU + attr->imgW / 2 * ((attr->startY + i) / 2) + attr->startX / 2, colorU, attr->thickness / 2);
            memset(dataV + attr->imgW / 2 * ((attr->startY + i) / 2) + attr->startX / 2, colorV, attr->thickness / 2);
            memset(dataU + attr->imgW / 2 * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) / 2, colorU, attr->thickness / 2);
            memset(dataV + attr->imgW / 2 * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) / 2, colorV, attr->thickness / 2);
        } else if (attr->imgFmt == LYN_PIX_FMT_NV12) {
            for (int j = 0; j < attr->thickness / 2; j++) {
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX + 2 * j) = colorU;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX + 2 * j + 1) = colorV;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) + 2 * j) = colorU;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) + 2 * j + 1) = colorV;
            }
        } else if (attr->imgFmt == LYN_PIX_FMT_NV21) {
            for (int j = 0; j < attr->thickness / 2; j++) {
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX + 2 * j) = colorV;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX + 2 * j + 1) = colorU;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) + 2 * j) = colorV;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) + 2 * j + 1) = colorU;
            }
        }
    }

    return 0;
}

int PluginDrawCustomColorBox(CustomColorBoxAttr *attr)
{
    if (attr == nullptr || attr->imgData == nullptr) {
        return lynEPTR;
    }

    if (attr->startX > attr->imgW ||
        attr->startY > attr->imgH ||
        attr->boxW + attr->startX > attr->imgW ||
        attr->boxH + attr->startY > attr->imgH) {
        LOG_PLUGIN_W("box point not in range, imgW %u imgH %u startX %u startY %u boxW %u boxH %u\n",
            attr->imgW, attr->imgH, attr->startX, attr->startY, attr->boxW, attr->boxH);
        return lynEINVARG;
    }

    attr->startX = (attr->startX / 2) * 2;
    attr->startY = (attr->startY / 2) * 2;
    attr->boxW = (attr->boxW / 2) * 2;
    attr->boxH = (attr->boxH / 2) * 2;

    if ((int)attr->boxW < 2 * attr->thickness || (int)attr->boxH < 2 * attr->thickness) {
        LOG_PLUGIN_W("box size less than twice of thickness, boxW %u boxH %u thickness %u\n",
            attr->boxW, attr->boxH, attr->thickness);
        return lynEINVARG;
    }

    if (attr->imgFmt != LYN_PIX_FMT_YUV420P &&
        attr->imgFmt != LYN_PIX_FMT_NV12 &&
        attr->imgFmt != LYN_PIX_FMT_NV21) {
        LOG_PLUGIN_W("format %d not support", attr->imgFmt);
        return lynEFORMAT;
    }

    unsigned char * dataY = attr->imgData;
    unsigned char * dataU = attr->imgData + attr->imgW * attr->imgH;
    unsigned char * dataV = attr->imgData + (attr->imgW * attr->imgH) * 5 / 4;
    unsigned char colorY = attr->colorY;
    unsigned char colorU = attr->colorU;
    unsigned char colorV = attr->colorV;

    for (int i = 0; i < attr->thickness; i++) {
        memset(dataY + attr->imgW * (attr->startY + i) + attr->startX, colorY, attr->boxW);
        memset(dataY + attr->imgW * (attr->startY + attr->boxH - attr->thickness + i) + attr->startX, colorY, attr->boxW);
        if (i % 2 != 0) { // uv 分量消减
            continue;
        }

        if (attr->imgFmt == LYN_PIX_FMT_YUV420P) {
            memset(dataU + attr->imgW / 2 * ((attr->startY + i) / 2) + attr->startX / 2, colorU, attr->boxW / 2);
            memset(dataV + attr->imgW / 2 * ((attr->startY + i) / 2) + attr->startX / 2, colorV, attr->boxW / 2);
            memset(dataU + attr->imgW / 2 * ((attr->startY + attr->boxH - attr->thickness + i) / 2) + attr->startX / 2, colorU, attr->boxW / 2);
            memset(dataV + attr->imgW / 2 * ((attr->startY + attr->boxH - attr->thickness + i) / 2) + attr->startX / 2, colorV, attr->boxW / 2);
        } else if (attr->imgFmt == LYN_PIX_FMT_NV12) {
            unsigned char * colorTop = dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX;
            unsigned char * colorBottom = dataU + attr->imgW* ((attr->startY + attr->boxH - attr->thickness + i) / 2) + attr->startX;
            for (uint32_t j = 0; j < attr->boxW / 2; j++) {
                *(colorTop + 2 * j) = colorU;
                *(colorTop + 2 * j + 1) = colorV;
                *(colorBottom + 2 * j) = colorU;
                *(colorBottom + 2 * j + 1) = colorV;
            }
        } else if (attr->imgFmt == LYN_PIX_FMT_NV21) {
            unsigned char * colorTop = dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX;
            unsigned char * colorBottom = dataU + attr->imgW* ((attr->startY + attr->boxH - attr->thickness + i) / 2) + attr->startX;
            for (uint32_t j = 0; j < attr->boxW / 2; j++) {
                *(colorTop + 2 * j) = colorV;
                *(colorTop + 2 * j + 1) = colorU;
                *(colorBottom + 2 * j) = colorV;
                *(colorBottom + 2 * j + 1) = colorU;
            }
        }
    }

    for (int i = attr->thickness; i < (int)attr->boxH - attr->thickness; i++) {
        memset(dataY + attr->imgW * (attr->startY + i) + attr->startX, colorY, attr->thickness);
        memset(dataY + attr->imgW * (attr->startY + i) + attr->startX + attr->boxW - attr->thickness, colorY, attr->thickness);

        if (i % 2 != 0) {
            continue;
        }

        if (attr->imgFmt == LYN_PIX_FMT_YUV420P) {
            memset(dataU + attr->imgW / 2 * ((attr->startY + i) / 2) + attr->startX / 2, colorU, attr->thickness / 2);
            memset(dataV + attr->imgW / 2 * ((attr->startY + i) / 2) + attr->startX / 2, colorV, attr->thickness / 2);
            memset(dataU + attr->imgW / 2 * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) / 2, colorU, attr->thickness / 2);
            memset(dataV + attr->imgW / 2 * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) / 2, colorV, attr->thickness / 2);
        } else if (attr->imgFmt == LYN_PIX_FMT_NV12) {
            for (int j = 0; j < attr->thickness / 2; j++) {
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX + 2 * j) = colorU;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX + 2 * j + 1) = colorV;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) + 2 * j) = colorU;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) + 2 * j + 1) = colorV;
            }
        } else if (attr->imgFmt == LYN_PIX_FMT_NV21) {
            for (int j = 0; j < attr->thickness / 2; j++) {
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX + 2 * j) = colorV;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + attr->startX + 2 * j + 1) = colorU;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) + 2 * j) = colorV;
                *(dataU + attr->imgW * ((attr->startY + i) / 2) + (attr->startX + attr->boxW - attr->thickness) + 2 * j + 1) = colorU;
            }
        }
    }

    return 0;
}

struct TextBitMap {
    uint8_t *buffer = nullptr;
    int w = 0;
    int h = 0;
    // char ch;
    wchar_t ch;
    FontSize fontSize;
};

namespace sampleFreeType
{
    static bool s_init = false;
    static int s_fontSize = 0;
    static std::mutex s_drawTextMux;
    static FT_Library ftLib;
    static FT_Face ftFace;
    static std::map<uint32_t, TextBitMap> s_textMap;

    int Init()
    {
        std::lock_guard<std::mutex> lock(s_drawTextMux);
        int ret = 0;
        if (s_init == false) {
            ret = FT_Init_FreeType(&ftLib);
            CHECK_RETURN(ret, lynEDRAWTEXT, "error ret " << ret);

            ret = FT_New_Face(ftLib, "/usr/share/Monaco_Linux.ttf", 0, &ftFace);
            CHECK_RETURN(ret, lynEDRAWTEXT, "error ret " << ret);

            ret = FT_Select_Charmap(ftFace, FT_ENCODING_UNICODE);
            CHECK_RETURN(ret, lynEDRAWTEXT, "error ret " << ret);
            s_init = true;
        }
        return ret;
    }

    TextBitMap* SelectSingleWchar(wchar_t ch, FontSize fontSize)
    {
        uint32_t key = ch;
        TextBitMap bitMap;
        key = (key << 8) + fontSize;
        std::lock_guard<std::mutex> lock(s_drawTextMux);
        if (s_fontSize != fontSize) {
            FT_Set_Char_Size(ftFace, 0, fontSize * 64, 0, 96);
            FT_Set_Pixel_Sizes(ftFace, 0, fontSize);
            s_fontSize = fontSize;
        }
        auto it = s_textMap.find(key);
        if (it == s_textMap.end()) {
            auto index = FT_Get_Char_Index(ftFace, ch);
            FT_Load_Glyph(ftFace, index, FT_LOAD_DEFAULT);
            FT_Render_Glyph(ftFace->glyph, FT_RENDER_MODE_NORMAL);
            bitMap.w = ftFace->glyph->bitmap.width;
            bitMap.h = ftFace->glyph->bitmap.rows;
            bitMap.buffer = (uint8_t *)malloc(bitMap.w * bitMap.h);
            bitMap.ch = ch;
            bitMap.fontSize = fontSize;
            // size += bitMap.w * bitMap.h;
            // printf("malloc total %d size %d font %d ch %c\n", size, bitMap.w * bitMap.h, bitMap.fontSize, bitMap.ch);
            memcpy(bitMap.buffer, ftFace->glyph->bitmap.buffer, bitMap.w * bitMap.h);
            s_textMap[key] = bitMap;
        }
        return &s_textMap[key];
    }

} // sampleFreeType

inline void yuvSetdata(uint8_t *YBuff, uint8_t *UVBuff, lynPixelFormat_t yuvType, uint32_t width, uint32_t height,
                 DarwPoint &drawPoint, uint8_t YColor, uint8_t UColor, uint8_t VColor, bool updateUV)
{
    *(YBuff + drawPoint.y * width + drawPoint.x) = YColor;

    // update U V
    uint32_t u_offset = 0, v_offset = 0;
    // uint32_t plane_size = width * height / 4;
    switch (yuvType) {
    case LYN_PIX_FMT_NV12: {
        u_offset = (drawPoint.y / 2) * width + drawPoint.x / 2 * 2;
        *(UVBuff + u_offset) = UColor;
        *(UVBuff + u_offset + 1) = VColor;
        return;
    } break;

    case LYN_PIX_FMT_NV21: {       
        v_offset = (drawPoint.y / 2) * width + drawPoint.x / 2 * 2;
        u_offset = v_offset + 1;
    } break;

    case LYN_PIX_FMT_YUV420P: {
        u_offset = drawPoint.y / 2 * width / 2 + drawPoint.x / 2;
        v_offset = u_offset + width * height / 4;
    } break;
    default: {
        return ;
    }
    }
    UVBuff[u_offset] = UColor;
    UVBuff[v_offset] = VColor;
}

int PluginDrawTextV2(DrawTextAttrV2 *attr)
{
    if (attr == nullptr || attr->imgData == nullptr || attr->text == nullptr) {
        return lynEPTR;
    }

    if (attr->imgFmt != LYN_PIX_FMT_YUV420P &&
        attr->imgFmt != LYN_PIX_FMT_NV12 &&
        attr->imgFmt != LYN_PIX_FMT_NV21) {
        LOG_PLUGIN_W("format %d not support\n", attr->imgFmt);
        return lynEFORMAT;
    }
    int ret = 0;
    ret = sampleFreeType::Init();
    if (ret != 0) {
        return ret;
    }

    unsigned char * data_y = attr->imgData;
    unsigned char * data_u = attr->imgData + attr->imgW * attr->imgH;
    // unsigned char * data_v = attr->imgData + (attr->imgW * attr->imgH) * 5 / 4;

    uint32_t startX = attr->startX;
    uint32_t startY = attr->startY;
    // uint32_t imgW = attr->imgW;
    uint32_t imgH = attr->imgH;
    int char_num = 0;
    uint32_t pix_x_base = startX;
    uint32_t pix_y_base = startY;

    uint8_t colorY = g_sampleColorMap[attr->color].colorY;
    uint8_t colorU = g_sampleColorMap[attr->color].colorU;
    uint8_t colorV = g_sampleColorMap[attr->color].colorV;

    uint8_t letterSpace = (attr->fontSize / SAMPLE_FONT_SIZE_18 < 2) ? 2 : attr->fontSize / SAMPLE_FONT_SIZE_18;
    for (wchar_t ch = *(attr->text + char_num); *(attr->text + char_num) != '\0'; char_num++) {
        ch = *(attr->text + char_num);
        if (ch == ' ') {
            pix_x_base += attr->fontSize / 2 + 2;
            continue;
        }
        auto bitMap = sampleFreeType::SelectSingleWchar(ch, attr->fontSize);

        int w = bitMap->w;
        int h = bitMap->h;
        pix_y_base = startY - h;

        unsigned char * data = bitMap->buffer;
        for (int j = 0; j < h; j++) {
            for (int i = 0; i < w; i++) {
                uint32_t pix_x = pix_x_base + i;
                uint32_t pix_y = pix_y_base + j;
                if (pix_y < 0 || pix_y >= imgH || pix_x >= attr->imgW || pix_x < 0) {
                    continue;
                }
                if (data[j * w + i] != 0) {
                    DarwPoint draw_point;
                    draw_point.x = pix_x;
                    draw_point.y = pix_y;
                    yuvSetdata(data_y, data_u, attr->imgFmt, attr->imgW, attr->imgH, draw_point,
                               colorY, colorU, colorV, true);
                }
            }
        }
        pix_x_base += w + letterSpace ;
    }
    return 0;
}

int PluginDrawText(DrawTextAttr *attr)
{
    if (attr == nullptr || attr->imgData == nullptr || attr->text == nullptr) {
        return lynEPTR;
    }
    wchar_t wText[100];
    auto len = std::mbstowcs(wText, attr->text, 100);
    if (len < 0) {
        return -1;
    }
    DrawTextAttrV2 attrV2;
    attrV2.fontSize = attr->fontSize;
    attrV2.imgData = attr->imgData;
    attrV2.imgFmt = attr->imgFmt;
    attrV2.imgH = attr->imgH;
    attrV2.imgW = attr->imgW;
    attrV2.startX = attr->startX;
    attrV2.startY = attr->startY;
    attrV2.text = wText;
    attrV2.color = SAMPLE_DRAW_COLOR_WHITE;
    return PluginDrawTextV2(&attrV2);
}



/**
 * @brief 画 y1 = y2 的横直线
 *
 * @param[] attr
 * @return int
 */
int DrawTransverseLine(DrawLineAttr *attr)
{
    int ret = 0;
    uint8_t *YBuff = attr->imgData;
    uint8_t *UVBuff = attr->imgData + attr->imgW * attr->imgH;
    unsigned char colorY = g_sampleColorMap[attr->color].colorY;
    unsigned char colorU = g_sampleColorMap[attr->color].colorU;
    unsigned char colorV = g_sampleColorMap[attr->color].colorV;
    uint32_t x0 = 0, x1 = 0;
    uint32_t y = attr->startPoint.y;
    if (attr->startPoint.x < attr->endPoint.x) {
        x0 = attr->startPoint.x;
        x1 = attr->endPoint.x;
    } else {
        x0 = attr->endPoint.x;
        x1 = attr->startPoint.x;
    }
    uint32_t length = x1 - x0;
    for (int i = 0; i < attr->thickness; i++) {
        memset(YBuff + attr->imgW * (y + i) + x0, colorY, length);
        if (i % 2 != 0) {  // uv 分量消减
            continue;
        }

        if (attr->imgFmt == LYN_PIX_FMT_YUV420P) {
            unsigned char *dataU = attr->imgData + attr->imgW * attr->imgH;
            unsigned char *dataV = attr->imgData + (attr->imgW * attr->imgH) * 5 / 4;
            memset(dataU + attr->imgW / 2 * ((y + i) / 2) + x0 / 2, colorU,
                   length / 2);
            memset(dataV + attr->imgW / 2 * ((y + i) / 2) + x0 / 2, colorV,
                   length / 2);
        } else if (attr->imgFmt == LYN_PIX_FMT_NV12) {
            unsigned char *colorTop = UVBuff + attr->imgW * ((y + i) / 2) + x0;
            for (uint32_t j = 0; j < length / 2; j++) {
                *(colorTop + 2 * j) = colorU;
                *(colorTop + 2 * j + 1) = colorV;
            }
        } else if (attr->imgFmt == LYN_PIX_FMT_NV21) {
            unsigned char *colorTop = UVBuff + attr->imgW * ((y + i) / 2) + x0;
            for (uint32_t j = 0; j < length / 2; j++) {
                *(colorTop + 2 * j) = colorV;
                *(colorTop + 2 * j + 1) = colorU;
            }
        }
    }
    return ret;
}

int PluginDrawLine(DrawLineAttr *attr)
{
    if (attr == nullptr || attr->imgData == nullptr) {
        return lynEPTR;
    }

    if (attr->startPoint.x > attr->imgW || attr->startPoint.y > attr->imgH ||
        attr->endPoint.x > attr->imgW || attr->endPoint.y > attr->imgH) {
        LOG_PLUGIN_W("point not in range, imgW %u imgH %u startX %u startY %u endX %u endY %u\n",
                     attr->imgW, attr->imgH, attr->startPoint.x, attr->startPoint.y,
                     attr->endPoint.x, attr->endPoint.y);
        return lynEINVARG;
    }

    if (attr->imgFmt != LYN_PIX_FMT_YUV420P &&
        attr->imgFmt != LYN_PIX_FMT_NV12 &&
        attr->imgFmt != LYN_PIX_FMT_NV21) {
        LOG_PLUGIN_W("format %d not support", attr->imgFmt);
        return lynEFORMAT;
    }

    if (attr->startPoint.y == attr->endPoint.y) {
        return DrawTransverseLine(attr);
    }

    int ret = 0;
    uint8_t *YBuff = attr->imgData;
    uint8_t *UVBuff = attr->imgData + attr->imgW * attr->imgH;
    // uint16_t i = 0;
    uint8_t colorY = g_sampleColorMap[attr->color].colorY;
    uint8_t colorU = g_sampleColorMap[attr->color].colorU;
    uint8_t colorV = g_sampleColorMap[attr->color].colorV;
    // uint32_t halfWidth = attr->imgW / 2;
    for (uint16_t i = 0; i < attr->thickness; i++) {
        uint16_t x0 = attr->startPoint.x, y0 = attr->startPoint.y;
        uint16_t x1 = attr->endPoint.x, y1 = attr->endPoint.y;
        x0 = (x0 >= attr->imgW) ? (x0 - attr->thickness) : x0;
        x1 = (x1 >= attr->imgW) ? (x1 - attr->thickness) : x1;
        y0 = (y0 >= attr->imgH) ? (y0 - attr->thickness) : y0;
        y1 = (y1 >= attr->imgH) ? (y1 - attr->thickness) : y1;

        uint16_t dx = (x0 > x1) ? (x0 - x1) : (x1 - x0);
        uint16_t dy = (y0 > y1) ? (y0 - y1) : (y1 - y0);

        if (dx <= dy) {
            x0 += i;
            x1 += i;
        } else {
            y0 += i;
            y1 += i;
        }

        int xstep = (x0 < x1) ? 1 : -1;
        int ystep = (y0 < y1) ? 1 : -1;
        int nstep = 0, eps = 0;

        DarwPoint draw_point;
        draw_point.x = x0;
        draw_point.y = y0;

        // bool updateUV = i == attr->thickness ? false : true;
        bool updateUV = true;
        // 布雷森汉姆算法画线
        if (dx > dy) {
            while (nstep <= dx) {
                yuvSetdata(YBuff, UVBuff, attr->imgFmt, attr->imgW,
                            attr->imgH, draw_point, colorY, colorU, colorV, updateUV);
                eps += dy;
                if ((eps << 1) >= dx) {
                    draw_point.y += ystep;
                    eps -= dx;
                }
                draw_point.x += xstep;
                nstep++;
            }
        } else {
            while (nstep <= dy) {
                yuvSetdata(YBuff, UVBuff, attr->imgFmt, attr->imgW,
                            attr->imgH, draw_point, colorY, colorU, colorV, updateUV);
                eps += dx;
                if ((eps << 1) >= dy) {
                    draw_point.x += xstep;
                    eps -= dy;
                }
                draw_point.y += ystep;
                nstep++;
            }
        }
    }
    return ret;
}

int lynDrawBoxAndText(lynDrawBoxAndTextPara *para)
{
    int ret = 0;
    uint8_t *imgData = (uint8_t *)lynPluginGetVirtAddr(para->imgData);
    lynBoxesInfo *boxesInfo = (lynBoxesInfo *)lynPluginGetVirtAddr(para->boxesInfo);

    for (uint32_t i = 0; i < boxesInfo->boxesNum; i++) {
        auto& box = boxesInfo->boxes[i];
        DrawColor boxColor = (box.alarm == 1) ? SAMPLE_DRAW_COLOR_RED : SAMPLE_DRAW_COLOR_BLUE;
        DrawBoxAttr boxAttr = {box.xmin, box.ymin, box.xmax - box.xmin, box.ymax - box.ymin,
            para->imgW, para->imgH, SAMPLE_DRAW_THICK_2, boxColor, para->imgFmt, imgData};
        ret = PluginDrawBox(&boxAttr);
        if (ret != 0) {
            break;
        }

        DrawTextAttrV2 textAttr = {box.xmin, box.ymin, para->imgW, para->imgH, para->imgFmt,
                                    SAMPLE_FONT_SIZE_24, boxColor, imgData, box.lable};
        ret = PluginDrawTextV2(&textAttr);
        if (ret != 0) {
            break;
        }
    }

    return ret;
}