从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(绘图设备封装)

目录

图像层的底层抽象——绘图设备抽象

如何抽象一个绘图设备?

桥接绘图设备,特化为OLED设备

题外话:设备的属性,与设计一个相似函数化简的通用办法

使用函数指针来操作设备

总结一下


图像层的底层抽象——绘图设备抽象

在上一篇博客中,我们完成了对设备层的抽象。现在,我们终于可以卖出雄心壮志的一步了!那就是尝试去完成一个最为基础的图形库。我们要做的,就是设计一个更加复杂的绘图设备。

为什么是绘图设备呢?我们程序员都是懒蛋,想要最大程度的复用代码,省最大的力气干最多的事情。所以,我们的图像框架在未来,还会使用LCD绘制,还会使用其他形形色色的绘制设备来绘制我们的图像。而不仅限于OLED。所以,让我们抽象一个可以绘制的设备而不是一个OLED设备,是非常重要的。

一个绘图设备,是OLED设备的的子集。他可以开启关闭,完成绘制操作,刷新绘制操作,清空绘制操作。仅此而已。

typedef void*   CCDeviceRawHandle;
typedef void*   CCDeviceRawHandleConfig;
​
// 初始化设备,设备需要做一定的初始化后才能绘制图形
typedef void(*Initer)(
    CCDeviceHandler* handler, 
    CCDeviceRawHandleConfig config);
​
// 清空设备
typedef void(*ClearDevice)(
    CCDeviceHandler* handler);
​
// 更新设备
typedef void(*UpdateDevice)(
    CCDeviceHandler* handler);
​
// 反色设备
typedef void(*ReverseDevice)(
    CCDeviceHandler* handler);
​
// 绘制点
typedef void(*SetPixel)(
    CCDeviceHandler* handler, uint16_t x, uint16_t y);
​
// 绘制面
typedef void(*DrawArea)(
    CCDeviceHandler* handler, 
    uint16_t x, uint16_t y, 
    uint16_t width, uint16_t height, uint8_t* sources
);
​
// 面操作(清空,反色,更新等等,反正不需要外来提供绘制资源的操作)
typedef void(*AreaOperation)(
    CCDeviceHandler* handler, 
    uint16_t x, uint16_t y, 
    uint16_t width, uint16_t height
);
​
// 这个比较新,笔者后面讲
typedef enum{
    CommonProperty_WIDTH,
    CommonProperty_HEIGHT,
    CommonProperty_SUPPORT_RGB
}CommonProperty;
​
// 获取资源的属性
typedef void(*FetchProperty)(CCDeviceHandler*, void*, CommonProperty p);
​
// 一个绘图设备可以完成的操作
// 提示,其实可以化简,一些函数指针(或者说方法)是没有必要存在的,思考一下如何化简呢?
typedef struct __DeviceOperations 
{
    Initer          init_function;
    ClearDevice     clear_device_function;
    UpdateDevice    update_device_function;
    SetPixel        set_pixel_device_function;
    ReverseDevice   reverse_device_function;
    DrawArea        draw_area_device_function;
    AreaOperation   clearArea_function;
    AreaOperation   updateArea_function;
    AreaOperation   reverseArea_function;
    FetchProperty   property_function;
}CCDeviceOperations;
​
// 一个绘图设备的最终抽象
typedef struct __DeviceProperty
{
    /* device type */
    CCDeviceType            device_type;
    /* device raw data handle */
    CCDeviceRawHandle       handle;
    /* device functions */
    CCDeviceOperations      operations;
}CCDeviceHandler;

设计上笔者是自底向上设计的,笔者现在打算自顶向下带大伙解读一下我的代码。

如何抽象一个绘图设备?

这个设备是什么?是一个OLED?还是一个LCD?

/* device type */
CCDeviceType            device_type;

这个设备的底层保存资源是什么?当我们动手准备操作的时候,需要拿什么进行操作呢?

    /* device raw data handle */
    CCDeviceRawHandle       handle;

你不需要在使用的时候关心他到底是什么,因为我们从头至尾都在使用接口进行操作,你只需要知道,一个绘图设备可以绘制图像,这就足够了

    /* device functions */
    CCDeviceOperations      operations;

这里是我们的命根子,一个绘图设备可以完成的操作。我们在之后的设计会大量的见到operations这个操作。

笔者的operations借鉴了Linux是如何抽象文件系统的代码。显然,一个良好的面对对象C编写规范的参考代码就是Linux的源码

下一步,就是DeviceType有哪些呢?目前,我们开发的是OLED,也就意味着只有OLED是一个合法的DeviceType

typedef enum{
    OLED_Type
}CCDeviceType;

最后,我们需要思考的是,如何定义一个绘图设备的行为呢?我们知道我们现在操作的就是一个OLED,所以,我们的问题实际上就转化成为:

当我们给定了一个明确的,是OLED设备的绘图设备的时候,怎么联系起来绘图设备和OLED设备呢?

答案还是回到我们如何抽象设备层的代码上,那就是根据我们的类型来选择我们的方法。

/* calling this is not encouraged! */
void __register_paintdevice(
    CCDeviceHandler* blank_handler, 
    CCDeviceRawHandle raw_handle, 
    CCDeviceRawHandleConfig config, 
    CCDeviceType type);
​
#define register_oled_paintdevice(handler, raw, config) \
    __register_paintdevice(handler, raw, config, OLED_Type)

所以,我们注册一个OLED的绘图设备,只需要调用接口register_oled_paintdevice就好了,提供一个干净的OLED_HANDLE和初始化OLED_HANDLE所需要的资源,我们的设备也就完成了初始化。

#include "Graphic/device_adapter/CCGraphic_device_oled_adapter.h"
#include "Graphic/CCGraphic_device_adapter.h"
​
void __register_paintdevice(
    CCDeviceHandler* blank_handler, 
    CCDeviceRawHandle raw_handle, 
    CCDeviceRawHandleConfig config, 
    CCDeviceType type)
{
    blank_handler->handle = raw_handle;
    blank_handler->device_type = type;
    switch(type)
    {
        case OLED_Type:
        {
            blank_handler->operations.init_function = 
                (Initer)init_device_oled;
            blank_handler->operations.clear_device_function =
                clear_device_oled;
            blank_handler->operations.set_pixel_device_function = 
                setpixel_device_oled;
            blank_handler->operations.update_device_function = 
                update_device_oled;
            blank_handler->operations.clearArea_function =
                clear_area_device_oled;
            blank_handler->operations.reverse_device_function =
                reverse_device_oled;
            blank_handler->operations.reverseArea_function = 
                reversearea_device_oled;
            blank_handler->operations.updateArea_function = 
                update_area_device_oled;
            blank_handler->operations.draw_area_device_function =
                draw_area_device_oled;
            blank_handler->operations.property_function = 
                property_fetcher_device_oled;
        }
        break;
    }
    blank_handler->operations.init_function(blank_handler, config);
}

这个仍然是最空泛的代码,我们只是简单的桥接了一下,声明我们的设备是OLED,还有真正完成桥接的文件:CCGraphic_device_oled_adapter文件没有给出来。所以,让我们看看实际上是如何真正的完成桥接的。

桥接绘图设备,特化为OLED设备

什么是桥接?什么是特化?桥接指的是讲一个抽象结合过度到另一个抽象上,在这里,我们讲绘图设备引渡到我们的OLED设备而不是其他更加宽泛的设备上去,而OLED设备属于绘图设备的一个子集,看起来,我们就像是把虚无缥缈的“绘图设备”落地了,把一个抽象的概念更加具体了。我们的聊天从“用绘图设备完成XXX”转向了“使用一个OLED作为绘图设备完成XXX”了。这就是特化,将一个概念明晰起来。

#include "Graphic/CCGraphic_device_adapter.h"
#include "OLED/Driver/oled_config.h"
​
/* 
 * 提供用于 OLED 设备的相关操作函数 
 */
​
/**
 * @struct CCGraphic_OLED_Config
 * @brief OLED 设备的配置结构体
 */
typedef struct {
    OLED_Driver_Type    createType;      // OLED 驱动类型(软 I2C、硬 I2C 等)
    void*               related_configs; // 与驱动相关的具体配置
} CCGraphic_OLED_Config;
​
/**
 * @brief 初始化 OLED 设备
 * @param blank 空的设备句柄,初始化后填充
 * @param onProvideConfigs OLED 配置参数指针,包含驱动类型及配置
 * 
 * @note 调用此函数时需要传递初始化好的配置(软 I2C 或硬 I2C 配置等)
 */
void init_device_oled(
    CCDeviceHandler* blank, CCGraphic_OLED_Config* onProvideConfigs);
​
/**
 * @brief 刷新整个 OLED 屏幕内容
 * @param handler 设备句柄
 */
void update_device_oled(CCDeviceHandler* handler);
​
/**
 * @brief 清空 OLED 屏幕内容
 * @param handler 设备句柄
 */
void clear_device_oled(CCDeviceHandler* handler);
​
/**
 * @brief 设置指定位置的像素点
 * @param handler 设备句柄
 * @param x 横坐标
 * @param y 纵坐标
 */
void setpixel_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y);
​
/**
 * @brief 清除指定区域的显示内容
 * @param handler 设备句柄
 * @param x 区域起点的横坐标
 * @param y 区域起点的纵坐标
 * @param width 区域宽度
 * @param height 区域高度
 */
void clear_area_device_oled(CCDeviceHandler* handler, 
        uint16_t x, uint16_t y, uint16_t width, uint16_t height);
​
/**
 * @brief 更新指定区域的显示内容
 * @param handler 设备句柄
 * @param x 区域起点的横坐标
 * @param y 区域起点的纵坐标
 * @param width 区域宽度
 * @param height 区域高度
 */
void update_area_device_oled(CCDeviceHandler* handler, 
        uint16_t x, uint16_t y, uint16_t width, uint16_t height);
​
/**
 * @brief 反转整个屏幕的显示颜色
 * @param handler 设备句柄
 */
void reverse_device_oled(CCDeviceHandler* handler);
​
/**
 * @brief 反转指定区域的显示颜色
 * @param handler 设备句柄
 * @param x 区域起点的横坐标
 * @param y 区域起点的纵坐标
 * @param width 区域宽度
 * @param height 区域高度
 */
void reversearea_device_oled(CCDeviceHandler* handler, 
        uint16_t x, uint16_t y, uint16_t width, uint16_t height);
​
/**
 * @brief 绘制指定区域的图像
 * @param handler 设备句柄
 * @param x 区域起点的横坐标
 * @param y 区域起点的纵坐标
 * @param width 区域宽度
 * @param height 区域高度
 * @param sources 图像数据源指针
 */
void draw_area_device_oled(
    CCDeviceHandler* handler, 
    uint16_t x, uint16_t y, 
    uint16_t width, uint16_t height, uint8_t* sources
);
​
/**
 * @brief 获取设备属性
 * @param handler 设备句柄
 * @param getter 属性获取指针
 * @param p 属性类型
 */
void property_fetcher_device_oled(
    CCDeviceHandler* handler, void* getter, CommonProperty p
);
​

好在代码实际上并不困难,具体的代码含义我写在下面了,可以参考看看

#include "Graphic/device_adapter/CCGraphic_device_oled_adapter.h"
#include "OLED/Driver/oled_base_driver.h"
​
/**
 * @brief 初始化 OLED 设备
 * 
 * 根据提供的配置(软 I2C、硬 I2C、软 SPI、硬 SPI)初始化 OLED 设备。
 * 
 * @param blank 空的设备句柄,初始化后填充
 * @param onProvideConfigs OLED 配置参数指针,包含驱动类型及具体配置
 */
void init_device_oled(
    CCDeviceHandler* blank, CCGraphic_OLED_Config* onProvideConfigs)
{
    OLED_Handle* handle = (OLED_Handle*)(blank->handle);
    OLED_Driver_Type type = onProvideConfigs->createType;
​
    switch(type)
    {
        case OLED_SOFT_IIC_DRIVER_TYPE:
            oled_init_softiic_handle(
                handle,
                (OLED_SOFT_IIC_Private_Config*) (onProvideConfigs->related_configs)
            );
        break;
​
        case OLED_HARD_IIC_DRIVER_TYPE:
            oled_init_hardiic_handle(
                handle, 
                (OLED_HARD_IIC_Private_Config*)(onProvideConfigs->related_configs));
        break;
​
        case OLED_SOFT_SPI_DRIVER_TYPE:
            oled_init_softspi_handle(
                handle,
                (OLED_SOFT_SPI_Private_Config*)(onProvideConfigs->related_configs)
            );
        break;
​
        case OLED_HARD_SPI_DRIVER_TYPE:
            oled_init_hardspi_handle(
                handle,
                (OLED_HARD_SPI_Private_Config*)(onProvideConfigs->related_configs)
            );
        break;
    }
}
​
/**
 * @brief 刷新整个 OLED 屏幕内容
 * 
 * @param handler 设备句柄
 */
void update_device_oled(CCDeviceHandler* handler)
{
    OLED_Handle* handle = (OLED_Handle*)handler->handle;
    oled_helper_update(handle);
}
​
/**
 * @brief 清空 OLED 屏幕内容
 * 
 * @param handler 设备句柄
 */
void clear_device_oled(CCDeviceHandler* handler)
{
    OLED_Handle* handle = (OLED_Handle*)handler->handle;
    oled_helper_clear_frame(handle);
}
​
/**
 * @brief 设置指定位置的像素点
 * 
 * @param handler 设备句柄
 * @param x 横坐标
 * @param y 纵坐标
 */
void setpixel_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y)
{
    OLED_Handle* handle = (OLED_Handle*)handler->handle;
    oled_helper_setpixel(handle, x, y);
}
​
/**
 * @brief 清除指定区域的显示内容
 * 
 * @param handler 设备句柄
 * @param x 区域起点的横坐标
 * @param y 区域起点的纵坐标
 * @param width 区域宽度
 * @param height 区域高度
 */
void clear_area_device_oled(CCDeviceHandler* handler, 
        uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
    OLED_Handle* handle = (OLED_Handle*)handler->handle;
    oled_helper_clear_area(handle, x, y, width, height);
}
​
/**
 * @brief 更新指定区域的显示内容
 * 
 * @param handler 设备句柄
 * @param x 区域起点的横坐标
 * @param y 区域起点的纵坐标
 * @param width 区域宽度
 * @param height 区域高度
 */
void update_area_device_oled(CCDeviceHandler* handler, 
        uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
    OLED_Handle* handle = (OLED_Handle*)handler->handle;
    oled_helper_update_area(handle, x, y, width, height);
}
​
/**
 * @brief 反转整个屏幕的显示颜色
 * 
 * @param handler 设备句柄
 */
void reverse_device_oled(CCDeviceHandler* handler)
{
    OLED_Handle* handle = (OLED_Handle*)handler->handle;
    oled_helper_reverse(handle);
}
​
/**
 * @brief 反转指定区域的显示颜色
 * 
 * @param handler 设备句柄
 * @param x 区域起点的横坐标
 * @param y 区域起点的纵坐标
 * @param width 区域宽度
 * @param height 区域高度
 */
void reversearea_device_oled(CCDeviceHandler* handler, 
        uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
    OLED_Handle* handle = (OLED_Handle*)handler->handle;
    oled_helper_reversearea(handle, x, y, width, height);
}
​
/**
 * @brief 绘制指定区域的图像
 * 
 * @param handler 设备句柄
 * @param x 区域起点的横坐标
 * @param y 区域起点的纵坐标
 * @param width 区域宽度
 * @param height 区域高度
 * @param sources 图像数据源指针
 */
void draw_area_device_oled(
    CCDeviceHandler* handler, 
    uint16_t x, uint16_t y, 
    uint16_t width, uint16_t height, uint8_t* sources
){
    OLED_Handle* handle = (OLED_Handle*)handler->handle;
    oled_helper_draw_area(handle, x, y, width, height, sources);
}
​
/**
 * @brief 获取 OLED 设备属性
 * 
 * @param handler 设备句柄
 * @param getter 属性获取指针
 * @param p 属性类型(如:高度、宽度、是否支持 RGB 等)
 */
void property_fetcher_device_oled(
    CCDeviceHandler* handler, void* getter, CommonProperty p
)
{
    OLED_Handle* handle = (OLED_Handle*)handler->handle;
    switch (p)
    {
    case CommonProperty_HEIGHT:
    {   
        int16_t* pHeight = (int16_t*)getter;
        *pHeight = oled_height(handle);
    } break;
​
    case CommonProperty_WIDTH:
    {
        int16_t* pWidth = (int16_t*)getter;
        *pWidth = oled_width(handle);
    } break;
​
    case CommonProperty_SUPPORT_RGB:
    {
        uint8_t* pSupportRGB = (uint8_t*)getter;
        *pSupportRGB = oled_support_rgb(handle);
    } break;
​
    default:
        break;
    }
}
题外话:设备的属性,与设计一个相似函数化简的通用办法

绘图设备有自己的属性,比如说告知自己的可绘图范围,是否支持RGB色彩绘图等等,我们的办法是提供一个对外暴露的可以访问的devicePropertyEnum

typedef enum{
    CommonProperty_WIDTH,
    CommonProperty_HEIGHT,
    CommonProperty_SUPPORT_RGB
}CommonProperty;

设计一个接口,这个接口函数就是FetchProperty

typedef void(*FetchProperty)(CCDeviceHandler*, void*, CommonProperty p);

上层框架代码提供一个承接返回值的void*和查询的设备以及查询类型,我们就返回这个设备的期望属性

/**
 * @brief 获取 OLED 设备属性
 * 
 * @param handler 设备句柄
 * @param getter 属性获取指针
 * @param p 属性类型(如:高度、宽度、是否支持 RGB 等)
 */
void property_fetcher_device_oled(
    CCDeviceHandler* handler, void* getter, CommonProperty p
)
{
    OLED_Handle* handle = (OLED_Handle*)handler->handle;
    switch (p)
    {
    case CommonProperty_HEIGHT:
    {   
        int16_t* pHeight = (int16_t*)getter;
        *pHeight = oled_height(handle);
    } break;
​
    case CommonProperty_WIDTH:
    {
        int16_t* pWidth = (int16_t*)getter;
        *pWidth = oled_width(handle);
    } break;
​
    case CommonProperty_SUPPORT_RGB:
    {
        uint8_t* pSupportRGB = (uint8_t*)getter;
        *pSupportRGB = oled_support_rgb(handle);
    } break;
​
    default:
        break;
    }
}

这个就是一种设计返回相似内容的数据的设计思路,将过多相同返回的函数简化为一个函数,将差异缩小到使用枚举宏而不是一大坨函数到处拉屎的设计方式

任务提示:笔者这里实际上做的不够好,你需要知道的是,我在这里是没有做错误处理的。啥意思?你必须让人家知道你返回的值是不是合法的,人家才知道这个值敢不敢用。

笔者提示您,两种办法:

  1. 返回值上动手脚:这个是笔者推介的,也是Linux设备代码中使用的,那就是将属性获取的函数签名返回值修改为uint8_t,或者更进一步的封装:

    typedef enum {
        FETCH_PROPERTY_FAILED;  // 0, YOU CAN USE AS FALSE, BUT NOT RECOMMENDED!
        FETCH_PROPERTY_SUCCESS; // 1, YOU CAN USE AS TRUE, BUT NOT RECOMMENDED!
    }FetchPropertyStatus;
    ​
    /**
     * @brief 获取 OLED 设备属性
     * 
     * @param handler 设备句柄
     * @param getter 属性获取指针
     * @param p 属性类型(如:高度、宽度、是否支持 RGB 等)
     * @return 
     */
    FetchPropertyStatus property_fetcher_device_oled(
        CCDeviceHandler* handler, void* getter, CommonProperty p
    )
    {
        OLED_Handle* handle = (OLED_Handle*)handler->handle;
        switch (p)
        {
        case CommonProperty_HEIGHT:
        {   
            int16_t* pHeight = (int16_t*)getter;
            *pHeight = oled_height(handle);
        } break;
    ​
        case CommonProperty_WIDTH:
        {
            int16_t* pWidth = (int16_t*)getter;
            *pWidth = oled_width(handle);
        } break;
    ​
        case CommonProperty_SUPPORT_RGB:
        {
            uint8_t* pSupportRGB = (uint8_t*)getter;
            *pSupportRGB = oled_support_rgb(handle);
        } break;
    ​
        default:
            return FETCH_PROPERTY_FAILED; // not supported property
        }
        return FETCH_PROPERTY_SUCCESS; // fetched value can be used for further
    }

    使用上,事情也就变得非常的简单,笔者后面的一个代码

        int16_t device_width = 0;
        device_handle->operations.property_function(
            device_handle, &device_width, CommonProperty_WIDTH
        );
        int16_t device_height = 0;
        device_handle->operations.property_function(
            device_handle, &device_height, CommonProperty_HEIGHT
        );

    也就可以更加合理的修改为

        FetchPropertyStatus status;
        // fetch the width property
        int16_t device_width = 0;
        status = device_handle->operations.property_function(
            device_handle, &device_width, CommonProperty_WIDTH
        );
        // check if the value valid
        if(!statue){
            // handling error, or enter HAL_Hard_Fault... anyway!
        }
        int16_t device_height = 0;
        statue = device_handle->operations.property_function(
            device_handle, &device_height, CommonProperty_HEIGHT
        );
        // check if the value valid
        if(!statue){
            // handling error, or enter HAL_Hard_Fault... anyway!
        }
        // now pass the check
        // use the variable directly
        ...

  2. 选取一个非法值。比如说

    #define INVALID_PROPERTY_VALUE      -1
    ...
    default:
        {   
            (int8_t*)value = (int8_t*)getter;
            value = INVALID_PROPERTY_VALUE;
        }

    但是显然不好!我们没办法区分:是不支持这个属性呢?还是设备的返回值确实就是-1呢?谁知道呢?所以笔者很不建议在这样的场景下这样做!甚至更糟糕的,如果是返回设备的长度,我们使用的是uint16_t接受,那么我们完全没办法区分究竟是设备是0xFFFF长,还是是非法值呢?我们一不小心把判断值的非法和值的含义本身混淆在一起了!

现在,我们就可以完成对一整个设备的抽象了。

使用函数指针来操作设备

笔者之前的代码已经反反复复出现了使用函数指针而不是调用函数来进行操作,从开销分析上讲,我们多了若干次的解引用操作,但是从封装上,我们明确的归属了函数隶属于绘图设备的方法,在极大量的代码下,这样起到了一种自说明的效果。

比起来,在业务层次(拿库做应用的层次,比如说开发一个OLED菜单,做一个恐龙奔跑小游戏,或者是绘制电棍突脸尖叫的动画),我们只需要强调是这个设备在绘图

device_handle->operations.updateArea_function(...);

而不是我们让绘图的是这个设备

updateArea_device(device_handle, ...);

显然前者更加的自然。

总结一下

其实,就是完成了对绘图设备的特化,现在,我们终于可以直接使用Device作为绘图设备而不是OLED_Handle,下一步,我们就开始真正的手搓设备绘制了。

目录导览

总览

协议层封装

OLED设备封装

绘图设备抽象

基础图形库封装

基础组件实现

动态菜单组件实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/962228.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【开源免费】基于SpringBoot+Vue.JS公交线路查询系统(JAVA毕业设计)

本文项目编号 T 164 ,文末自助获取源码 \color{red}{T164,文末自助获取源码} T164,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

【Unity3D】实现横版2D游戏角色二段跳、蹬墙跳、扶墙下滑

目录 一、二段跳、蹬墙跳 二、扶墙下滑 一、二段跳、蹬墙跳 GitHub - prime31/CharacterController2D 下载工程后直接打开demo场景:DemoScene(Unity 2019.4.0f1项目环境) Player物体上的CharacterController2D,Mask添加Wall层…

讯飞智作 AI 配音技术浅析(二):深度学习与神经网络

讯飞智作 AI 配音技术依赖于深度学习与神经网络,特别是 Tacotron、WaveNet 和 Transformer-TTS 模型。这些模型通过复杂的神经网络架构和数学公式,实现了从文本到自然语音的高效转换。 一、Tacotron 模型 Tacotron 是一种端到端的语音合成模型&#xff…

初始化mysql报错cannot open shared object file: No such file or directory

报错展示 我在初始化msyql的时候报错:mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory 解读: libaio包的作用是为了支持同步I/O。对于数据库之类的系统特别重要,因此…

DeepSeek介绍

目录 前言 1.介绍一下你自己 2.什么是CUDA CUDA的核心特点: CUDA的工作原理: CUDA的应用场景: CUDA的开发工具: CUDA的局限性: 3.在AI领域,PTX是指什么 1. PTX 的作用 2. PTX 与 AI 的关系 3. …

python学opencv|读取图像(五十一)使用修改图像像素点上BGR值实现图像覆盖效果

【1】引言 前序学习了图像的得加方法,包括使用add()函数直接叠加BGR值、使用bitwise()函数对BGR值进行按位计算叠加和使用addWeighted()函数实现图像加权叠加至少三种方法。文章链接包括且不限于: python学opencv|读取图像(四十二&#xff…

【硬件介绍】三极管工作原理(图文+典型电路设计)

什么是三极管? 三极管,全称为双极型晶体三极管,是一种广泛应用于电子电路中的半导体器件。它是由三个掺杂不同的半导体材料区域组成的,这三个区域分别是发射极(E)、基极(B)和集电极&…

【解决方案】MuMu模拟器移植系统进度条卡住98%无法打开

之前在Vmware虚拟机里配置了mumu模拟器,现在想要移植到宿主机中 1、虚拟机中的MuMu模拟器12-1是目标系统,对应的目录如下 C:\Program Files\Netease\MuMu Player 12\vms\MuMuPlayer-12.0-1 2、Vmware-虚拟机-设置-选项,启用共享文件夹 3、复…

C++中常用的十大排序方法之1——冒泡排序

成长路上不孤单😊😊😊😊😊😊 【😊///计算机爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于C中常用的排序方法之——冒泡排序的相关…

开源2+1链动模式AI智能名片S2B2C商城小程序:利用用户争强好胜心理促进分享行为的策略研究

摘要:随着互联网技术的快速发展和社交媒体的普及,用户分享行为在企业营销中的作用日益凸显。本文旨在探讨如何利用用户的争强好胜心理,通过开源21链动模式AI智能名片S2B2C商城小程序(以下简称“小程序”)促进用户分享行…

DeepSeek-R1环境搭建推理测试

引子 这两天国货之光DeepSeek-R1火爆出圈,凑个热闹。过来看看 aha moment(顿悟时刻)的神奇,OK,我们开始吧。 一、模型介绍 1月20日,中国AI公司深度求索(DeepSeek)发布的DeepSeek-…

【深度分析】微软全球裁员计划不影响印度地区,将继续增加当地就业机会

当微软的裁员刀锋掠过全球办公室时,班加罗尔的键盘声却愈发密集——这场资本迁徙背后,藏着数字殖民时代最锋利的生存法则。 表面是跨国公司的区域战略调整,实则是全球人才市场的地壳运动。微软一边在硅谷裁撤年薪20万美金的高级工程师&#x…

架构技能(六):软件设计(下)

我们知道,软件设计包括软件的整体架构设计和模块的详细设计。 在上一篇文章(见 《架构技能(五):软件设计(上)》)谈了软件的整体架构设计,今天聊一下模块的详细设计。 模…

unity使用内置videoplayer打包到安卓手机进行视频播放

1.新建UI,新建RawImage在画布当作视频播放的显示载体 2.新建VideoPlayer 3.新建Render Texture作为连接播放器视频显示和幕布的渲染纹理 将Render Texture同时挂载在VideoPlayer播放器和RawImage上。这样就可以将显示的视频内容在RawImage上显示出来了。 问题在于&a…

LLMs之RAG:解读RAG主流的七类架构(Naive RAG/Retrieve-and-rerank/Multimodal RAG/GraphRAG/HybridRAG/Agentic RAG(Ro

LLMs之RAG:解读RAG主流的七类架构(Naive RAG/Retrieve-and-rerank/Multimodal RAG/GraphRAG/HybridRAG/Agentic RAG(Router)/Agentic RAG(Multi-Agent)) 目录 解读RAG主流的七类架构(Naive RAG/Retrieve-and-rerank/Multimodal RAG/GraphRAG/HybridRAG/Agentic RAG…

99.20 金融难点通俗解释:中药配方比喻马科维茨资产组合模型(MPT)

目录 0. 承前1. 核心知识点拆解2. 中药搭配比喻方案分析2.1 比喻的合理性 3. 通俗易懂的解释3.1 以中药房为例3.2 配方原理 4. 实际应用举例4.1 基础配方示例4.2 效果说明 5. 注意事项5.1 个性化配置5.2 定期调整 6. 总结7. 代码实现 0. 承前 本文主旨: 本文通过中…

python算法和数据结构刷题[1]:数组、矩阵、字符串

一画图二伪代码三写代码 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中)-CSDN博客 算法通关手册(LeetCode) | 算法通关手册(LeetCode) (itcharge.cn) 面试经典 150 题 - 学习计…

EWM 变更库存类型

目录 1 简介 2 配置 3 业务操作 1 简介 一般情况下 EWM 标准收货流程是 ROD(Ready on Dock) --> AFS(Avaiable for Sale),对应 AG 001 --> AG 002,对应库存类型 F1 --> F2。 因业务需要反向进行的时候,AFS --> ROD,AG 002 --> AG 001,库存类型 F2…

B站吴恩达机器学习笔记

机器学习视频地址: 4.5 线性回归中的梯度下降_哔哩哔哩_bilibili 损失函数学习地址: 损失函数选择 选凸函数的话,会收敛到全局最小值。证明凸函数用Hessian矩阵。凸函数定义:两点连线比线上所有点都大。 batch理解&#xff1…

SpringBoot 数据访问(MyBatis)

SpringBoot 数据访问(MyBatis) 向 SQL 语句传参 #{} 形式 #{}:如果传过来的值是字符串类型。那两边会自动加上 单引号。当传递给 #{} 的参数值是非字符串类型(如整数、浮点数、布尔值等),MyBatis 不会为这些值添加引…