一、SDL_CreateText
在渲染器内创建纹理。
语法
SDL_Texture* SDL_CreateTexture(SDL_Renderer* renderer, Uint32 format, int access, int w, int h)
参数
renderer | 纹理要归属的渲染器 |
format[ | 像素格式,SDL_PixelFormatenum中的枚举值 |
access | 访问方法,SDL_TextureAccess中的枚举值。更多见注释中“支持的access值” |
w | 像素单位的纹理宽度 |
h | 像素单位的纹理高度 |
返回值
如果成功创建,返回指向该纹理的指针,否则NULL。可能失败的原因:1)没有激活的上下文;2)不支持的像素格式;3)指定的宽度或高度超出范围。可调用SDL_GetError获得更多错误信息。
范例
<Example>
注释
1. 支持的access值
值 | |
SDL_TEXTUREACCESS_STATIC | 很少修改,不能上锁。修改指的是调用SDL_UpdateTexture修改像素数据 |
SDL_TEXTUREACCESS_STREAMING | 经常修改,能上锁。修改概念等同SDL_TEXTUREACCESS_STATIC |
SDL_TEXTUREACCESS_TARGET | 可作为复制时的目标纹理。复制操作指SDL_RenderCopy、SDL_RenderCopyEx |
2. 缩放方式(scaleMode)
调用该函数前,可设置SDL_HINT_RENDER_SCALE_QUALITY选择希望的缩放方式。以下表格描述了选择到某个值时的条件。
使用它的条件 | |
GL_NEAREST | 没有设置SDL_HINT_RENDER_SCALE_QUALITY,或空,或是nearest |
GL_LINEAR | 除以上之外 |
3. 纹理链表
渲染器用开环双向链表(SDL_Texture* textures)来存储内中纹理。纹理一旦创建,就会被加入链表,删除则从链表中移除。新建纹理被加到链表头部。
4. 执行逻辑
- 申请一块SDL_Texture大小的内存。
- 检查format是否是渲染器能支持的格式。opengl es2支持的第一种式是SDL_PIXELFORMAT_ARGB8888。
- 调用CreateTexture,opengs es2时对应的是GLES2_CreateTexture。
接下描述GLES2_CreateTexture函数逻辑。
1. 根据像素格式得到format、type。对SDL_PIXELFORMAT_ARGB8888,它们的值分别是format=GL_RGBA,type = GL_UNSIGNED_BYTE。
2. 创建要挂接到该纹理的私有结构GLES2_TextureData,填充该结构。
字段 | 语义 | 实例 |
texture_type | GL_TEXTURE_2D | 固定是GL_TEXTURE_2D |
pixel_format | 来自上面计算出的format | GL_RGBA |
pixel_type | 来自上面计算出的type | GL_UNSIGNED_BYTE |
yuv | 根据格式判断,是IYUV、YV12置1,否则0 | |
nv12 | 根据格式判断,是NV12、NV21置1,否则0 |
3. access==SDL_TEXTUREACCESS_STREAMING,会分配个blob用于存储图像数据,pixel_data指向这个数据块。
4. 初始化该纹理,以下是涉及到的opengl api
glGenTextures(1, &data->texture); glActiveTexture(GL_TEXTURE0); // 激活GL_TEXTURE0纹理单元,以便后续的gl调用是这个活动单元。 glBindTexture(data->texture_type, data.texture); glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode); // 如何计算scaleMode见上面的缩放方式 glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode); glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(data->texture_type, 0, format, texture->w, texture->h, 0, format, type, NULL); // 加载图像数据。像素字段置的是NULL,此时并没有真正上传数据。
5. access==SDL_TEXTUREACCESS_TARGET,会创建帧缓冲区对象fbo。 要注意的是,并不是一个纹理就会创建一个fbo,而是根据尺寸,多少种尺才对应多少个fbo,正因为这个原因,在SDL_DestroyTexture时不执行删除fbo,而是要等到SDL_DestroyRenderer时一并删除。创建fbo会调用以下gl api。
glGenFramebuffers(1, &result->FBO);
二、SDL_DestroyTexture
删除指定纹理
语法
void SDL_DestroyTexture(SDL_Texture* texture)
参数
texture | 要删除的纹理 |
返回值
void
范例
<Example>
注释
如果该纹理是渲染器的目标纹理,调用SDL_SetRenderTarget把目标纹理置NULL。
从渲染器的纹理链表删除texture对应的节点。纹理链表见SDL_CreateTexture中的“纹理链表”。
调用DestroyTexture,opengl es2对应GLES2_DestroyTexture。
void GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) { GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; ...... if (tdata) { glDeleteTextures(1, &tdata->texture); ...... SDL_free(tdata->pixel_data); SDL_free(tdata); texture->driverdata = NULL; } }
它不删除fbo,为什么不删见SDL_CreateTexture注释部分的“access==SDL_TEXTUREACCESS_TARGET”。
三、SDL_RenderReadPixels
从当前帧缓冲区读像素数据
语法
int SDL_RenderReadPixels(SDL_Renderer* renderer, const SDL_Rect* rect, Uint32 format, void* pixels, int pitch)
参数
renderer | 要操作的渲染器 |
rect | 位在帧缓冲区的矩形,函数目标就是取该矩形块内的像素 |
format | 生成的像素数据的格式 |
pixels | app须预分配、用于存放生成的像素数据的存储区 |
pitch | 生成的像素块的行跨距。[color=Red]不是帧缓冲区的行跨距。 |
返回值
0表示成功。否则是一个负值,调用SDL_GetError可获得错误的更多信息。
范例
surface dst = create_neutral_surface(area.w, area.h); { surface_lock dst_lock(dst); uint32_t format = get_neutral_pixel_format().format; SDL_RenderReadPixels(get_renderer(), &area, format, dst->pixels, SDL_BYTESPERPIXEL(format) * area.w); }
读当前帧缓冲区,把area块内像素搬到dst->pixels。dst->pixels中的像素格式是get_neutral_pixel_format().format。
注释
这是个非常慢的函数,不要经常调用。怎么个慢法,参考这贴子:[url=http://www.libsdl.cn/bbs/forum.php?mod=viewthread&tid=49&extra=page%3D1]SDL_UpdateTexture、SDL_RenderCopy、SDL_RenderReadPixels的花费时间[/url]
窗口帧缓冲区和目标帧缓冲区
SDL_RenderReadPixels支持从两种帧缓冲区读取数据,渲染器的target变量控制了要读取是哪一种。当是目标帧缓冲区时,有一些特殊限定。
- 目标帧缓冲区对应纹理的像素格式须是4分量、4字节。
- SDL_RenderReadPixels不会改变像素格式,它直接以纹理格存放在pixels。因为这个原因,参数format其实只要表示这是“4分量、4字节”就行,而pitch总是要等于w*4。