(4)SDL渲染开发

SDL渲染开发

  • 1. SDL简介
  • 2. 环境搭建
    • 2.1 windows
    • 2.2 Linux
  • 3. SDL子系统
    • 3.1 eg1 创建窗口
  • 4:SDL显示
    • 4.1 显示图片
    • 4.2 绘制长方形显示
  • 5. SDL事件
  • 6. SDL多线程
    • 6.1 接口演示
    • 6.2 yuv显示
    • 6.3 pcm播放

1. SDL简介

  SDL(Simple DirectMedia Layer)是一个跨平台开发库,旨在通过 OpenGL 和 Direct3D 提供对音频、键盘、鼠标、游戏杆和图形硬件的低级访问。它被视频播放软件、模拟器和热门游戏(包括Valve的获奖产品和许多Humble Bundle游戏)所使用。SDL 正式支持 Windows、macOS、Linux、iOS 和 Android。其他平台的支持可以在源代码中找到。SDL 是用 C 编写的,可以与 C++ 直接配合使用,并且还有几种其他语言的绑定,包括 C# 和 Python。
  SDL 2.0 在zlib 许可 下发布。此许可允许您在任何软件中自由使用 SDL。

2. 环境搭建

https://github.com/libsdl-org/SDL/releases

2.1 windows

  1. 下载SDL2-devel-2.30.3-VC.zip(版本自己选即可) (我使用qt+cmake所以下面这些没用到)
  2. 添加环境变量
  3. 打开VS创建项目,进入VC++目录设置包含目录和库目录
  4. 进入链接器的输入,添加附加依赖项(库目录下的库)

2.2 Linux

  1. 下载 SDL2-2.30.3.tar.gz
  2. 解压然后执行命令
    ./configure (--prefix=...可以选择放置的目录)
    make
    sudo make install
    
  3. 如果出现Could not initialize SDL - No available video device(Did you set the DISPLAY variable?)需要安装x11库文件
    sudo apt-get install libx11-dev
    sudo apt-get install xorg-dev
    

3. SDL子系统

SDL将功能分成下列数个子系统(subsystem):
◼ SDL_INIT_TIMER:定时器
◼ SDL_INIT_AUDIO:音频
◼ SDL_INIT_VIDEO:视频
◼ SDL_INIT_JOYSTICK:摇杆
◼ SDL_INIT_HAPTIC:触摸屏
◼ SDL_INIT_GAMECONTROLLER:游戏控制器
◼ SDL_INIT_EVENTS:事件
◼ SDL_INIT_EVERYTHING:包含上述所有选项

3.1 eg1 创建窗口

直接看代码, 创建一个持续5s的窗口

  • CMakeLists.txt
cmake_minimum_required(VERSION 3.5)project(01-sdl-basic LANGUAGES C)
# 下面两个宏变量代表的是一个地址,在这里,为了记忆,都写在这里了
include_directories(${CMAKE_SOURCE_DIR}/SDL2-2.30.3/include)
link_directories(${PROJECT_SOURCE_DIR}/SDL2-2.30.3/lib/x64)add_executable(01-sdl-basic main.c)target_link_libraries(01-sdl-basicSDL2
)
  • main.c
#include <stdio.h>
#include <SDL.h>#undef main
int main()
{SDL_Init(SDL_INIT_VIDEO);  //初始化SDL为视频子系统SDL_Window* win = SDL_CreateWindow("my sdl",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,480, 360,SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); //创建窗口if (!win) {SDL_Log("SDL_CreateWindow error:", SDL_GetError());SDL_Quit();return 1;}SDL_Delay(5000);SDL_DestroyWindow(win);//销毁窗口SDL_Quit(); //释放资源return 0;
}

4:SDL显示

◼ SDL_Init():初始化SDL系统
◼ SDL_CreateWindow():创建窗口SDL_Window
◼ SDL_CreateRenderer():创建渲染器SDL_Renderer
◼ SDL_CreateTexture():创建纹理SDL_Texture
◼ SDL_UpdateTexture():设置纹理的数据
◼ SDL_RenderCopy():将纹理的数据拷贝给渲染器
◼ SDL_RenderPresent():显示
◼ SDL_Delay():工具函数,用于延时
◼ SDL_Quit():退出SDL系统

4.1 显示图片

#include <stdio.h>
#include <SDL.h>#undef main   //一定要写啊
int main()
{if (SDL_Init(SDL_INIT_VIDEO) != 0) {SDL_Log("SDL_Init error", SDL_GetError());return 1;}SDL_Window* win = SDL_CreateWindow("lena!",SDL_WINDOWPOS_CENTERED,  //居中SDL_WINDOWPOS_CENTERED,640, 480,SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (win == NULL) {SDL_Log("SDL_CreateWindow error", SDL_GetError());SDL_Quit();return 1;}// 创建一个将绘制到窗口的渲染器,-1 指定我们要加载任何一个// 视频驱动程序支持我们传递的标志// 标志: SDL_RENDERER_ACCELERATED:我们想使用硬件加速渲染// SDL_RENDERER_PRESENTVSYNC:我们希望渲染器的当前功能(更新屏幕)是与显示器的刷新率同步SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);if (ren == NULL) {SDL_Log("SDL_CreateRenderer error", SDL_GetError());SDL_DestroyWindow(win);SDL_Quit();return 1;}const char* imagePath = "./LenaRGB.bmp";SDL_Surface *bmp = SDL_LoadBMP(imagePath);if (bmp == NULL){SDL_DestroyRenderer(ren);SDL_DestroyWindow(win);SDL_Log("SDL_LoadBMP error", SDL_GetError());SDL_Quit();return 1;}//To use a hardware accelerated texture for rendering we can create one from//the surface we loadedSDL_Texture *tex = SDL_CreateTextureFromSurface(ren, bmp);//We no longer need the surfaceSDL_FreeSurface(bmp);if (tex == NULL){SDL_DestroyRenderer(ren);SDL_DestroyWindow(win);SDL_Log("SDL_CreateTextureFromSurface error", SDL_GetError());SDL_Quit();return 1;}//A sleepy rendering loop, wait for 3 seconds and render and present the screen each timefor (int i = 0; i < 3; ++i){//First clear the rendererSDL_RenderClear(ren);//Draw the textureSDL_RenderCopy(ren, tex, NULL, NULL);//Update the screenSDL_RenderPresent(ren);//Take a quick break after all that hard workSDL_Delay(1000);}//Clean up our objects and quitSDL_DestroyTexture(tex);SDL_DestroyRenderer(ren);SDL_DestroyWindow(win);SDL_Quit();return 0;
}

4.2 绘制长方形显示

cmake跟之前一样,注意把sdl.dll放到可执行文件同级目录即可
这里我注释了纹理相关代码,这样也是可以的
SDL_Texture 与SDL_Renderer相似,也是一种缓冲区。只不过它存放的不是真正的像素数据,而是存放的图像的描述信息。当渲染纹理时,SDL以这些描述信息为数据,底层通过OpenGL、D3D 或 Metal操作GPU,最终绘制出与SDL_Renderer一样的图形,且效率更高(因为它是GPU硬件计算的)。

存储RGB和存储纹理的区别:比如一个从左到右由红色渐变到蓝色的矩形,用存储RGB的话就需要把矩形中每个点的具体颜色值存储下来;而纹理只是一些描述信息,比如记录了矩形的大小、起始颜色、终止颜色等信息,显卡可以通过这些信息推算出矩形块的详细信息。所以相对于存储RGB而已,存储纹理占用的内存要少的多。

#include <iostream>
#include <SDL.h>
using namespace std;#undef main
int main()
{if (SDL_Init(SDL_INIT_VIDEO) != 0) {SDL_Log("SDL_Init error");return 1;}SDL_Window *win  = SDL_CreateWindow("长方形",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,640, 480,SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (win == nullptr) {SDL_Log("SDL_CreateWindow error");SDL_Quit();return 1;}SDL_Renderer* ren = SDL_CreateRenderer(win, -1, 0); //基于窗口创建渲染器if (ren == nullptr) {SDL_Log("SDL_CreateRenderer error");SDL_DestroyWindow(win);SDL_Quit();return 1;}SDL_Texture* texture = SDL_CreateTexture(ren,SDL_PIXELFORMAT_ARGB8888,SDL_TEXTUREACCESS_TARGET,640, 480);if (texture == nullptr) {SDL_DestroyRenderer(ren);SDL_DestroyWindow(win);SDL_Quit();return 1;}SDL_Rect rect{};rect.w = 50;rect.h = 50;int cnt = 0;while (true) {rect.x = rand() % 600;rect.y = rand() % 400;//SDL_SetRenderTarget(ren, texture);  //设置渲染器为纹理目标SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); //设置黑色背景SDL_RenderClear(ren); //清屏为我们设置的颜色SDL_RenderDrawRect(ren, &rect); //绘制长方形SDL_SetRenderDrawColor(ren, 255, 255, 255, 255); //白色长方形SDL_RenderFillRect(ren, &rect);// SDL_SetRenderTarget(ren, nullptr); //恢复默认, 渲染目标为窗口// SDL_RenderCopy(ren, texture, nullptr, nullptr); //拷贝到cpuSDL_RenderPresent(ren); //输出到窗口SDL_Delay(300);if (cnt++ > 30) {break;}}SDL_DestroyTexture(texture);SDL_DestroyRenderer(ren);SDL_DestroyWindow(win);SDL_Quit();return 0;
}

5. SDL事件

◼ 函数
• SDL_WaitEvent():等待一个事件
• SDL_PushEvent():发送一个事件
• SDL_PumpEvents():将硬件设备产生的事件放入事件队列,用于读取事件,在调用该函数之前,必须调 用SDL_PumpEvents搜集键盘等事件
• SDL_PeepEvents():从事件队列提取一个事件
◼ 数据结构
• SDL_Event:代表一个事件

看代码:显示鼠标的坐标,捕获键盘事件
注意,我这里试了case SDLK_q只能检测到大写字母,而case 'w’则忽略大小写了

#include <iostream>
#include <SDL.h>
using namespace std;#define FF_SDL_EVENT SDL_USEREVENT+1#undef main
int main()
{if (SDL_Init(SDL_INIT_VIDEO) != 0) {SDL_Log("SDL_Init error");return 1;}SDL_Window *win  = SDL_CreateWindow("事件",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,640, 480,SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (win == nullptr) {SDL_Log("SDL_CreateWindow error");SDL_Quit();return 1;}SDL_Renderer* ren = SDL_CreateRenderer(win, -1, 0); //基于窗口创建渲染器if (ren == nullptr) {SDL_Log("SDL_CreateRenderer error");SDL_DestroyWindow(win);SDL_Quit();return 1;}SDL_SetRenderDrawColor(ren, 0, 0, 255, 255); //设置蓝色背景SDL_RenderClear(ren); //清屏为我们设置的颜色SDL_RenderPresent(ren); //输出到窗口SDL_Event event;bool exit = false;while (!exit){while(SDL_PollEvent(&event)) {if (event.type == SDL_KEYUP) {switch (event.key.keysym.sym){case 'w':printf("key w down=============\n");SDL_Event event_w;event_w.type = FF_SDL_EVENT;SDL_PushEvent(&event_w);break;case SDLK_q:printf("key q down=============\n");SDL_Event event_q;event_q.type = FF_SDL_EVENT;SDL_PushEvent(&event_q);break;case SDLK_ESCAPE:printf("=================");SDL_Event event_esc;event_esc.type = FF_SDL_EVENT;SDL_PushEvent(&event_esc);break;default:printf("key down 0x%x\n", event.key.keysym.sym);break;}} else if (event.type == SDL_MOUSEMOTION) {//printf("mouse movie (%d,%d)\n", event.button.x, event.button.y);} else if (event.type == FF_SDL_EVENT) {exit = true;}}}SDL_DestroyRenderer(ren);SDL_DestroyWindow(win);SDL_Quit();return 0;
}

SDL_PollEvent和SDL_WaitEvent区别
除了SDL_PollEvent方法去取消息外,还有SDL_WaitEvent方法。顾名思义,该方法会阻塞当前调用的线程,直到取出一个消息为止。

SDL_PollEvent:
SDL_PollEvent函数是一个非阻塞函数,它会不断地检查 SDL 事件队列,直到有事件产生。如果有事件到达,它将将其从队列中取出并返回。如果没有事件,SDL_PollEvent会返回0。这种方式可以在事件到达时立即处理,而不需要等待。但是,如果程序需要等待某个特定事件,这种方法可能不够灵活。

SDL_WaitEvent:
SDL_WaitEvent函数是一个阻塞函数,它会等待 SDL 事件队列中的事件。直到有事件到达时,它才会返回。与SDL_PollEvent不同,SDL_WaitEvent会在没有事件时阻塞等待,直到事件到达或超时。这种方式在需要等待特定事件(例如用户输入)时非常有用。但是,如果事件处理程序在等待事件时需要执行其他任务,程序可能会变得不够高效。

6. SDL多线程

6.1 接口演示

◼ SDL线程创建:SDL_CreateThread
◼ SDL线程等待:SDL_WaitThead
◼ SDL互斥锁:SDL_CreateMutex/SDL_DestroyMutex
◼ SDL锁定互斥:SDL_LockMutex/SDL_UnlockMutex
◼ SDL条件变量(信号量):SDL_CreateCond/SDL_DestoryCond
◼ SDL条件变量(信号量)等待/通知:SDL_CondWait/SDL_CondSingal

#include <stdio.h>
#include <unistd.h>
#include <SDL.h>SDL_mutex* s_lock = NULL;
SDL_cond* s_cond = NULL;int thread_work(void *arg)
{SDL_LockMutex(s_lock);printf("<=============thread working sleep\n");flushall();sleep(10);  //用来测试获取锁printf("<=============thread working wait\n");//释放锁,并等待signalSDL_CondWait(s_cond, s_lock); //另一个线程(1)发送signal和(2)释放lock后,这个函数退出printf("<===========thread_work receive signal, continue to do ~_~!!!\n");printf("<===========thread_work end\n");SDL_UnlockMutex(s_lock);return 0;
}#undef main
int main()
{s_lock = SDL_CreateMutex();s_cond = SDL_CreateCond();SDL_Thread *t = SDL_CreateThread(thread_work, "hello world", NULL);if (!t) {SDL_Log("error: ", SDL_GetError());return -1;}for (int i = 0; i < 2; ++i) {sleep(2);printf("main execute------>\n");}printf("main SDL_LockMutex(s_lock) before ====================>\n");SDL_LockMutex(s_lock);  // 获取锁,但是子线程还拿着锁printf("main ready send signal====================>\n");printf("main SDL_CondSignal(s_cond) before ====================>\n");SDL_CondSignal(s_cond); // 发送信号,唤醒等待的线程printf("main SDL_CondSignal(s_cond) after ====================>\n");sleep(10);SDL_UnlockMutex(s_lock);// 释放锁,让其他线程可以拿到锁printf("main SDL_UnlockMutex(s_lock) after ====================>\n");SDL_WaitThread(t, NULL);SDL_DestroyMutex(s_lock);SDL_DestroyCond(s_cond);return 0;
}

6.2 yuv显示

#include <stdio.h>
#include <SDL.h>#define REFRESH_EVENT (SDL_USEREVENT + 1)  //请求刷新事件
#define QUIT_EVENT (SDL_USEREVENT + 2)  //请求刷新事件int thread_exit_flag = 0;
static int yuv_width = 320;
static int yuv_height = 240;int refresh_video_timer(void* data)
{while (!thread_exit_flag) {SDL_Event event;event.type = REFRESH_EVENT;SDL_PushEvent(&event);SDL_Delay(40);}thread_exit_flag = 0;SDL_Event event;event.type = QUIT_EVENT;SDL_PushEvent(&event);return 0;
}#undef main
int main()
{//初始化SDLif (SDL_Init(SDL_INIT_VIDEO) != 0) {SDL_Log("SDL_Init error");return -1;}// 创建窗口SDL_Window* win = SDL_CreateWindow("yuv播放器", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (!win) {SDL_Log("SDL_CreateWindow error");goto FLAG;}//创建渲染器SDL_Renderer* ren = SDL_CreateRenderer(win, -1, 0);if (!ren) {SDL_Log("SDL_CreateRenderer error");goto FLAG;}// 基于渲染器创建纹理SDL_Texture* texture = SDL_CreateTexture(ren, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, 320, 240);if (!texture) {SDL_Log("SDL_CreateTexture error");goto FLAG;}//分配空间// 我们测试的文件是YUV420P格式uint32_t y_frame_len = yuv_width * yuv_height;uint32_t u_frame_len = yuv_width * yuv_height / 4;uint32_t v_frame_len = yuv_width * yuv_height / 4;uint32_t yuv_frame_len = y_frame_len + u_frame_len + v_frame_len;uint8_t* video_buf = (uint8_t*)malloc(yuv_frame_len);if (!video_buf) {SDL_Log("分配空间失败");goto FLAG;}//打开yuv文件FILE* fd = fopen("./yuv420p_320x240.yuv", "rb");if (!fd) {goto FLAG; //C和C++的goto不太一样}// 创建刷新线程SDL_Thread* t = SDL_CreateThread(refresh_video_timer, NULL, NULL);SDL_Event event;                            // 事件SDL_Rect rect;                              // 矩形int win_width = yuv_width, win_height = yuv_height;while (1) {// 接收事件SDL_WaitEvent(&event);if (event.type == REFRESH_EVENT) {size_t video_buff_len = fread(video_buf, 1, yuv_frame_len, fd);if (video_buff_len <= 0) {SDL_Log("从文件读取数据失败");goto FLAG;}// 设置纹理的数据SDL_UpdateTexture(texture, NULL, video_buf, yuv_width);// 显示的区域rect.x = 0;rect.y = 0;float w_ratio = win_width * 1.0 /yuv_width;float h_ratio = win_height * 1.0 /yuv_height;rect.w = 320 * w_ratio;rect.h = 240 * h_ratio;// 清除当前显示SDL_RenderClear(ren);// 将纹理的数据拷贝给渲染器SDL_RenderCopy(ren, texture, NULL, &rect);// 显示SDL_RenderPresent(ren);} else if (event.type == SDL_WINDOWEVENT) {//If ResizeSDL_GetWindowSize(win, &win_width, &win_height);printf("SDL_WINDOWEVENT win_width:%d, win_height:%d\n",win_width,win_height );} else if (event.type == SDL_QUIT) {thread_exit_flag = 1;} else if (event.type == QUIT_EVENT) {break;}}FLAG:thread_exit_flag = 1;      // 保证线程能够退出// 释放资源if(t)SDL_WaitThread(t, NULL); // 等待线程退出if(video_buf)free(video_buf);if(fd)fclose(fd);if(texture)SDL_DestroyTexture(texture);if(ren)SDL_DestroyRenderer(ren);if(win)SDL_DestroyWindow(win);SDL_Quit();return 0;
}

6.3 pcm播放

打开音频设备

int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired, 
SDL_AudioSpec * obtained); 
// desired:期望的参数。
// obtained:实际音频设备的参数,一般情况下设置为NULL即可。

主要用到的结构体SDL_AudioSpec:

 typedef struct SDL_AudioSpec {int freq; // 音频采样率SDL_AudioFormat format; // 音频数据格式Uint8 channels; // 声道数: 1 单声道, 2 立体声Uint8 silence; // 设置静音的值,因为声音采样是有符号的,所以0当然就是这个值Uint16 samples; // 音频缓冲区中的采样个数,要求必须是2的n次Uint16 padding; // 考虑到兼容性的一个参数Uint32 size; // 音频缓冲区的大小,以字节为单位SDL_AudioCallback callback; // 填充音频缓冲区的回调函数void *userdata; // 用户自定义的数据
} SDL_AudioSpec;

SDL_AudioCallback

// userdata:SDL_AudioSpec结构中的用户自定义数据,一般情况下可以不用。
// stream:该指针指向需要填充的音频缓冲区。
// len:音频缓冲区的大小(以字节为单位)1024*2*2。
void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 *stream, int len);

播放音频数据

// 当pause_on设置为0的时候即可开始播放音频数据。设置为1的时候,将会
播放静音的值。
void SDLCALL SDL_PauseAudio(int pause_on);

代码示例

#include <stdio.h>
#include <SDL.h>// 每次读取2帧数据, 以1024个采样点一帧 2通道 16bit采样点为例
#define PCM_BUFFER_SIZE (1024*2*2*2)// 目前读取的位置
static Uint8 *s_audio_pos = NULL;
// 缓存结束位置
static Uint8 *s_audio_end = NULL;//音频设备回调函数
void handler_audio_pcm(void *udata, Uint8 *stream, int len)
{SDL_memset(stream, 0, len);if(s_audio_pos >= s_audio_end) // 数据读取完毕{return;}// 数据够了就读预设长度,数据不够就只读部分(不够的时候剩多少就读取多少)int remain_buffer_len = s_audio_end - s_audio_pos;len = (len < remain_buffer_len) ? len : remain_buffer_len;// 拷贝数据到stream并调整音量SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME/8);printf("len = %d\n", len);s_audio_pos += len;  // 移动缓存指针
}#undef main
int main()
{if (SDL_Init(SDL_INIT_AUDIO)) {SDL_Log("sdl init err");return -1;}// 打开pcmFILE* audio_fd = fopen("./44100_16bit_2ch.pcm", "rb");if (!audio_fd) {SDL_Log("fopen err");goto FLAG;}uint8_t* audio_buf = (uint8_t*)malloc(PCM_BUFFER_SIZE);// 音频参数设置SDL_AudioSpec spec;spec.freq = 44100;spec.format = AUDIO_S16SYS;spec.channels = 2;spec.samples = 1024;spec.callback = handler_audio_pcm;spec.userdata = NULL;//打开音频设备if(SDL_OpenAudio(&spec, NULL)){fprintf(stderr, "Failed to open audio device, %s\n", SDL_GetError());goto FLAG;}//play audioSDL_PauseAudio(0);int data_count = 0;// 每次缓存的长度size_t read_buffer_len = 0;while(1){// 从文件读取PCM数据read_buffer_len = fread(audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);if(read_buffer_len == 0){break;}data_count += read_buffer_len; // 统计读取的数据总字节数printf("now playing %10d bytes data.\n",data_count);s_audio_end = audio_buf + read_buffer_len;    // 更新buffer的结束位置s_audio_pos = audio_buf;  // 更新buffer的起始位置//the main thread wait for a momentwhile(s_audio_pos < s_audio_end){SDL_Delay(10);  // 等待PCM数据消耗}}printf("play PCM finish\n");// 关闭音频设备SDL_CloseAudio();FLAG:if (audio_buf) {free(audio_buf);}if (audio_fd) {fclose(audio_fd);}SDL_Quit();return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

导出excle表

文章目录 导出excle表需求场景引入依赖具体代码 导出excle表 需求场景 假设我们有一个需求&#xff0c;现在数据库中有一些用户信息&#xff0c;我们想要把这些信息导出到excle表格中&#xff0c;然后存储到本地磁盘中。要求&#xff1a;excle表格的第一行需要有黄色背景&…

2024-06-11 Unity Shader 开发入门5 —— CG 语法

文章目录 1 CG 语法基础1.1 CG 结构1.2 数据类型1.2.1 基础数据类型1.2.2 复合数据类型1.2.3 特殊数据类型 1.3 操作符1.3.1 Swizzle 操作符1.3.2 向量/矩阵转换 1.4 运算符1.5 流程控制语句1.6 函数1.6.1 有返回值的函数1.6.2 有返回值的函数 2 顶点/片元着色器2.1 着色器结构…

ClickHouse数据库对比、适用场景与入门指南

本文全面对比了ClickHouse与其他数据库&#xff08;如StarRocks、HBase、MySQL、Hive、Elasticsearch等&#xff09;的性能、功能、适用场景&#xff0c;并提供了ClickHouse的教学入门指南&#xff0c;旨在帮助读者选择合适的数据库产品并快速掌握ClickHouse的使用。 文章目录 …

2千泰中英泰语词汇对照含MP3真人读音

越来越多的人到泰国旅游&#xff0c;今天这一份数据就是服务于此&#xff0c;包含了2000条泰语的日常生活词汇&#xff0c;并且每条记录都含有真人发音MP3对应。 有分100个章节&#xff0c;每个章节共有20条记录&#xff0c;非常适合一章节一章节的学习&#xff0c;具体章节有&…

mysql在linux下安装与配置

一、MySQL 安装 本教程的系统平台&#xff1a;CentOS release 6.6 (Final) 64位。 1、安装编译工具及库文件 yum -y install gcc gcc-c make autoconf libtool-ltdl-devel gd-devel freetype-devel libxml2-devel libjpeg-devel libpng-devel openssl-devel curl-devel bison…

《大数据分析》期末考试整理

一、单项选择题&#xff08;1*9&#xff09; 1.大数据发展历程&#xff1a;出现阶段、热门阶段和应用阶段 P2 2.大数据影响 P3 1&#xff09;大数据对科学活动的影响 2&#xff09;大数据对思维方式的影响 3&#xff09;大数据对社会发展的影响 4&#xff09;大数…

C语言适不适合新手学习?

C 语言&#xff0c;尽管从整体上看相对较为简单&#xff0c;但在实际编写 C 程序的过程中&#xff0c;会涉及到诸如指针等一些细节方面&#xff0c;这确实可能会给新手带来一定程度的困扰和挑战。不过&#xff0c;我们需要明确的是&#xff0c;这种挑战从某种意义上来说是具有积…

Web前端设计风格论文:探索、创新与挑战

Web前端设计风格论文&#xff1a;探索、创新与挑战 在数字化时代的浪潮中&#xff0c;Web前端设计风格正逐渐成为塑造用户体验的关键因素。本文将从四个方面、五个方面、六个方面和七个方面深入探讨Web前端设计风格的发展趋势、创新实践以及所面临的挑战。 四个方面&#xff…

atcoder ABC 358-B题详解

atcoder ABC 358-B题详解 Problem Statement At the entrance of AtCoder Land, there is a single ticket booth where visitors line up to purchase tickets one by one. The purchasing process takes A seconds per person. Once the person at the front of the line f…

paddleocr实验过程

切割数据 Paddleocr/PPOCRLabel/gen_ocr_train_val_test.py&#xff1b;切割后的数据在train_data中&#xff0c;注意切割后的数据前缀默认是当前目录&#xff0c;因此建议切割数据在liunx中进行。建议在liunx中指定 python gen_ocr_train_val_test.py --datasetRootPathXX …

锁存器的工作原理及其在FPGA设计中的注意事项

锁存器&#xff08;Latch&#xff09;是数字电子中常用的一种基本元件&#xff0c;用于在特定的时间点或条件下“锁存”或保存输入的数据值。锁存器对脉冲电平敏感&#xff0c;它只在输入脉冲的高电平&#xff08;或低电平&#xff09;期间对输入信号敏感并改变状态。在数字电路…

React native 使用Animated 优化连续setState 性能问题

再部分场景下我们需要连续更新state刷新页面。一般情况刷新使用setstate没有问题&#xff0c;当需要连续刷新的情况会有明显的性能问题。 场景&#xff1a;自定义可拖动抽屉组件 新增需求在抽屉活动是更新主页面组件样式&#xff0c;此时需要动态传递抽屉高度修改主页组件属性…

vba学习系列(5)--指定区域指定字符串计数

系列文章目录 文章目录 系列文章目录前言一、需求背景二、vba自定义函数1.引入库 总结 前言 一、需求背景 想知道所有客诉项目里面什么项目最多&#xff0c;出现过多少次。 二、vba自定义函数 1.引入库 引用&#xff1a; CountCharInRange(区域,“字符串”) Function CountCh…

AWS无服务器 应用程序开发—第十二章 小结

在使用 AWS 无服务器架构进行开发时,需要注意的事项、易错点、性能优化建议和付费相关的信息: 前端开发(React.js) 注意点和易错点 CORS(跨域资源共享)问题:确保 API 网关设置了正确的 CORS 头。 环境配置:使用 .env 文件来管理环境变量。 构建优化:使用代码分割和…

人有时候不逼自己一把,永远不知道自己有多牛逼!

大家好&#xff0c;我是墨云&#xff0c;一位看起来不像搞技术的IT男。 之前写过一篇文章&#xff0c;从零开始&#xff0c;如何在3个月内吸引3500用户&#xff1a;我的私域运营秘诀&#xff0c;现如今&#xff0c;我们的私域社群成员数已逾五千人。&#x1f46b; 今天的主题&a…

MySQL -- 优化

1. 查询优化 使用索引 示例&#xff1a;有一个包含数百万用户的表&#xff0c;名为 users&#xff0c;常见的查询是通过 email 字段查找用户。 CREATE INDEX idx_email ON users(email);通过创建索引 idx_email&#xff0c;SELECT * FROM users WHERE email exampleexample…

【漏洞复现】红海云eHR PtFjk.mob 任意文件上传漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

【图解IO与Netty系列】Netty编解码器、TCP粘包拆包问题处理、Netty心跳检测机制

Netty编解码器、TCP粘包拆包问题处理、Netty心跳检测机制 Netty编解码器编码器解码器编解码器Netty提供的现成编解码器 TCP粘包拆包问题处理Netty心跳检测机制 Netty编解码器 网络传输是以字节流的形式传输的&#xff0c;而我们的应用程序一般不会直接对字节流进行处理&#x…

Verilog-Behavior Level 和 RTL Level 和 GATE Level的区别

硬件设计中对硬件的描述可以具有不同的抽象级别&#xff0c;以Verilog为例&#xff1a; Behavior Level。描述的是硬件的行为&#xff0c;当我们在看到如下关键字时就是行为级别的代码&#xff1a;#&#xff0c;wait&#xff0c;while&#xff0c;force&#xff0c;release等&…

System-Verilog 实现DE2-115 流水灯

文章目录 一、什么是SystemVerilog二、代码实现实现结果 一、什么是SystemVerilog SystemVerilog是一种硬件描述语言(HDL)&#xff0c;它用于设计和验证电子系统&#xff0c;特别是在集成电路(IC)和系统级芯片(SoC)的设计过程中。SystemVerilog是Verilog语言的一个超集&#xf…