=====================================================

SDL源码分析系列文章列表:

SDL2源码分析1:初始化(SDL_Init())

SDL2源码分析2:窗体(SDL_Window)

SDL2源码分析3:渲染器(SDL_Renderer)

SDL2源码分析4:纹理(SDL_Texture)

SDL2源码分析5:更新纹理(SDL_UpdateTexture())

SDL2源码分析6:拷贝到渲染器(SDL_RenderCopy())

SDL2源码分析7:显示(SDL_RenderPresent())

SDL2源码分析8:视频显示总结

=====================================================

上一篇文章分析了SDL更新纹理像素数据的函数SDL_UpdateTexture()。这篇文章继续分析SDL的源码。本文分析SDL纹理拷贝到渲染目标的函数SDL_RenderCopy()。

SDL播放视频的代码流程例如以下所看到的。

初始化: 

SDL_Init(): 初始化SDL。 
SDL_CreateWindow(): 创建窗体(Window)。 
SDL_CreateRenderer(): 基于窗体创建渲染器(Render)。 
SDL_CreateTexture(): 创建纹理(Texture)。

循环渲染数据: 

SDL_UpdateTexture(): 设置纹理的数据。 
SDL_RenderCopy(): 纹理复制给渲染器。 
SDL_RenderPresent(): 显示。

上篇文章分析了该流程中的第5个函数SDL_UpdateTexture()。本文继续分析该流程中的第6个函数SDL_RenderCopy()。

SDL_RenderCopy()

函数简单介绍

SDL使用SDL_RenderCopy()将纹理数据复制给渲染目标。SDL_RenderCopy()的原型例如以下。

int SDLCALL SDL_RenderCopy(SDL_Renderer * renderer,
SDL_Texture * texture,
const SDL_Rect * srcrect,
const SDL_Rect * dstrect);

參数的含义例如以下。
renderer:渲染目标。
texture:输入纹理。
srcrect:选择输入纹理的一块矩形区域作为输入。设置为NULL的时候整个纹理作为输入。
dstrect:选择渲染目标的一块矩形区域作为输出。设置为NULL的时候整个渲染目标作为输出。

成功的话返回0,失败的话返回-1。

函数调用关系图

SDL_RenderCopy()关键函数的调用关系能够用下图表示。

上面的图片不太清晰,更清晰的图片上传到了相冊里面:

http://my.csdn.net/leixiaohua1020/album/detail/1793911

把相冊里面的图片保存下来就能够得到清晰的图片了。

源码分析

SDL_RenderCopy()的源码位于render\SDL_render.c中。例如以下所看到的。

int SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_Rect * dstrect)
{
SDL_Rect real_srcrect = { 0, 0, 0, 0 };
SDL_Rect real_dstrect = { 0, 0, 0, 0 };
SDL_FRect frect; CHECK_RENDERER_MAGIC(renderer, -1);
CHECK_TEXTURE_MAGIC(texture, -1); if (renderer != texture->renderer) {
return SDL_SetError("Texture was not created with this renderer");
} real_srcrect.x = 0;
real_srcrect.y = 0;
real_srcrect.w = texture->w;
real_srcrect.h = texture->h;
if (srcrect) {
if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
return 0;
}
} SDL_RenderGetViewport(renderer, &real_dstrect);
real_dstrect.x = 0;
real_dstrect.y = 0;
if (dstrect) {
if (!SDL_HasIntersection(dstrect, &real_dstrect)) {
return 0;
}
real_dstrect = *dstrect;
} if (texture->native) {
texture = texture->native;
} /* Don't draw while we're hidden */
if (renderer->hidden) {
return 0;
} frect.x = real_dstrect.x * renderer->scale.x;
frect.y = real_dstrect.y * renderer->scale.y;
frect.w = real_dstrect.w * renderer->scale.x;
frect.h = real_dstrect.h * renderer->scale.y; return renderer->RenderCopy(renderer, texture, &real_srcrect, &frect);
}

从源码中能够看出,SDL_RenderCopy()的大致流程例如以下。

1. 检查输入參数的合理性。
2. 调用SDL_Render的RenderCopy ()方法复制纹理到渲染目标。

这一步是整个函数的核心。

以下我们具体看一下几种不同的渲染器的RenderCopy()的方法。

1. Direct3D

Direct3D 渲染器中相应RenderCopy()的函数是D3D_RenderCopy(),它的源码例如以下所看到的(位于render\direct3d\SDL_render_d3d.c)。

static int D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_FRect * dstrect)
{
D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
D3D_TextureData *texturedata;
LPDIRECT3DPIXELSHADER9 shader = NULL;
float minx, miny, maxx, maxy;
float minu, maxu, minv, maxv;
DWORD color;
Vertex vertices[4];
HRESULT result; if (D3D_ActivateRenderer(renderer) < 0) {
return -1;
} texturedata = (D3D_TextureData *)texture->driverdata;
if (!texturedata) {
SDL_SetError("Texture is not currently available");
return -1;
} minx = dstrect->x - 0.5f;
miny = dstrect->y - 0.5f;
maxx = dstrect->x + dstrect->w - 0.5f;
maxy = dstrect->y + dstrect->h - 0.5f; minu = (float) srcrect->x / texture->w;
maxu = (float) (srcrect->x + srcrect->w) / texture->w;
minv = (float) srcrect->y / texture->h;
maxv = (float) (srcrect->y + srcrect->h) / texture->h; color = D3DCOLOR_ARGB(texture->a, texture->r, texture->g, texture->b); vertices[0].x = minx;
vertices[0].y = miny;
vertices[0].z = 0.0f;
vertices[0].color = color;
vertices[0].u = minu;
vertices[0].v = minv; vertices[1].x = maxx;
vertices[1].y = miny;
vertices[1].z = 0.0f;
vertices[1].color = color;
vertices[1].u = maxu;
vertices[1].v = minv; vertices[2].x = maxx;
vertices[2].y = maxy;
vertices[2].z = 0.0f;
vertices[2].color = color;
vertices[2].u = maxu;
vertices[2].v = maxv; vertices[3].x = minx;
vertices[3].y = maxy;
vertices[3].z = 0.0f;
vertices[3].color = color;
vertices[3].u = minu;
vertices[3].v = maxv; D3D_SetBlendMode(data, texture->blendMode); D3D_UpdateTextureScaleMode(data, texturedata, 0); result =
IDirect3DDevice9_SetTexture(data->device, 0, (IDirect3DBaseTexture9 *)
texturedata->texture);
if (FAILED(result)) {
return D3D_SetError("SetTexture()", result);
} if (texturedata->yuv) {
shader = data->ps_yuv; D3D_UpdateTextureScaleMode(data, texturedata, 1);
D3D_UpdateTextureScaleMode(data, texturedata, 2); result =
IDirect3DDevice9_SetTexture(data->device, 1, (IDirect3DBaseTexture9 *)
texturedata->utexture);
if (FAILED(result)) {
return D3D_SetError("SetTexture()", result);
} result =
IDirect3DDevice9_SetTexture(data->device, 2, (IDirect3DBaseTexture9 *)
texturedata->vtexture);
if (FAILED(result)) {
return D3D_SetError("SetTexture()", result);
}
} if (shader) {
result = IDirect3DDevice9_SetPixelShader(data->device, shader);
if (FAILED(result)) {
return D3D_SetError("SetShader()", result);
}
}
result =
IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2,
vertices, sizeof(*vertices));
if (FAILED(result)) {
return D3D_SetError("DrawPrimitiveUP()", result);
}
if (shader) {
result = IDirect3DDevice9_SetPixelShader(data->device, NULL);
if (FAILED(result)) {
return D3D_SetError("SetShader()", result);
}
}
return 0;
}

从代码中能够看出,D3D_RenderCopy()函数依照运行的顺序调用了例如以下函数:

D3D_ActivateRenderer():激活渲染器。

其内部使用Direct3D的API函数IDirect3DDevice9_BeginScene()開始一个D3D的场景。
D3D_SetBlendMode():设置渲染器状态。

其内部使用Direct3D的API函数IDirect3DDevice9_SetRenderState()设置渲染器的状态。

D3D_UpdateTextureScaleMode():设置纹理採样方式。其内部调用使用Direct3D的API函数IDirect3DDevice9_SetSamplerState()设置D3D的纹理採样方式。
IDirect3DDevice9_SetTexture():Direct3D的API。用于设置当前启用的纹理。
IDirect3DDevice9_SetPixelShader():Direct3D的API。用于设置使用的像素着色器。

IDirect3DDevice9_DrawPrimitiveUP():Direct3D的API,用于渲染。

上述几个函数中,前3个函数是SDL中的函数,后3个函数是Direct3D的API。

在此附上前三个函数的代码。

D3D_ActivateRenderer():激活渲染器。

static int D3D_ActivateRenderer(SDL_Renderer * renderer)
{
D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
HRESULT result; if (data->updateSize) {
SDL_Window *window = renderer->window;
int w, h; SDL_GetWindowSize(window, &w, &h);
data->pparams.BackBufferWidth = w;
data->pparams.BackBufferHeight = h;
if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
data->pparams.BackBufferFormat =
PixelFormatToD3DFMT(SDL_GetWindowPixelFormat(window));
} else {
data->pparams.BackBufferFormat = D3DFMT_UNKNOWN;
}
if (D3D_Reset(renderer) < 0) {
return -1;
} data->updateSize = SDL_FALSE;
}
if (data->beginScene) {
result = IDirect3DDevice9_BeginScene(data->device);
if (result == D3DERR_DEVICELOST) {
if (D3D_Reset(renderer) < 0) {
return -1;
}
result = IDirect3DDevice9_BeginScene(data->device);
}
if (FAILED(result)) {
return D3D_SetError("BeginScene()", result);
}
data->beginScene = SDL_FALSE;
}
return 0;
}

D3D_SetBlendMode():设置渲染器状态。

static void D3D_SetBlendMode(D3D_RenderData * data, int blendMode)
{
switch (blendMode) {
case SDL_BLENDMODE_NONE:
IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
FALSE);
break;
case SDL_BLENDMODE_BLEND:
IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
TRUE);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
D3DBLEND_SRCALPHA);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
D3DBLEND_INVSRCALPHA);
if (data->enableSeparateAlphaBlend) {
IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA,
D3DBLEND_ONE);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA,
D3DBLEND_INVSRCALPHA);
}
break;
case SDL_BLENDMODE_ADD:
IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
TRUE);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
D3DBLEND_SRCALPHA);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
D3DBLEND_ONE);
if (data->enableSeparateAlphaBlend) {
IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA,
D3DBLEND_ZERO);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA,
D3DBLEND_ONE);
}
break;
case SDL_BLENDMODE_MOD:
IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
TRUE);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
D3DBLEND_ZERO);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
D3DBLEND_SRCCOLOR);
if (data->enableSeparateAlphaBlend) {
IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA,
D3DBLEND_ZERO);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA,
D3DBLEND_ONE);
}
break;
}
}

D3D_UpdateTextureScaleMode():设置纹理採样方式。

static void D3D_UpdateTextureScaleMode(D3D_RenderData *data, D3D_TextureData *texturedata, unsigned index)
{
if (texturedata->scaleMode != data->scaleMode[index]) {
IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_MINFILTER,
texturedata->scaleMode);
IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_MAGFILTER,
texturedata->scaleMode);
data->scaleMode[index] = texturedata->scaleMode;
}
}

2. OpenGL

OpenGL渲染器中相应RenderCopy()的函数是GL_RenderCopy(),它的源码例如以下所看到的(位于render\opengl\SDL_render_gl.c)。

static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_FRect * dstrect)
{
GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
GLfloat minx, miny, maxx, maxy;
GLfloat minu, maxu, minv, maxv; GL_ActivateRenderer(renderer); data->glEnable(texturedata->type);
if (texturedata->yuv) {
data->glActiveTextureARB(GL_TEXTURE2_ARB);
data->glBindTexture(texturedata->type, texturedata->vtexture); data->glActiveTextureARB(GL_TEXTURE1_ARB);
data->glBindTexture(texturedata->type, texturedata->utexture); data->glActiveTextureARB(GL_TEXTURE0_ARB);
}
data->glBindTexture(texturedata->type, texturedata->texture); if (texture->modMode) {
GL_SetColor(data, texture->r, texture->g, texture->b, texture->a);
} else {
GL_SetColor(data, 255, 255, 255, 255);
} GL_SetBlendMode(data, texture->blendMode); if (texturedata->yuv) {
GL_SetShader(data, SHADER_YV12);
} else {
GL_SetShader(data, SHADER_RGB);
} minx = dstrect->x;
miny = dstrect->y;
maxx = dstrect->x + dstrect->w;
maxy = dstrect->y + dstrect->h; minu = (GLfloat) srcrect->x / texture->w;
minu *= texturedata->texw;
maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
maxu *= texturedata->texw;
minv = (GLfloat) srcrect->y / texture->h;
minv *= texturedata->texh;
maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
maxv *= texturedata->texh; data->glBegin(GL_TRIANGLE_STRIP);
data->glTexCoord2f(minu, minv);
data->glVertex2f(minx, miny);
data->glTexCoord2f(maxu, minv);
data->glVertex2f(maxx, miny);
data->glTexCoord2f(minu, maxv);
data->glVertex2f(minx, maxy);
data->glTexCoord2f(maxu, maxv);
data->glVertex2f(maxx, maxy);
data->glEnd(); data->glDisable(texturedata->type); return GL_CheckError("", renderer);
}

从代码中能够看出。GL_RenderCopy()函数调用了OpenGL的API函数glActiveTexture(),glBindTexture()创建了一个纹理。而且使用GL_SetBlendMode(),GL_SetShader()设置了有关的一些參数。

有一点须要注意,在OpenGL渲染器中。假设输入像素格式是YUV,就会使用3个纹理。

3. Software

Software渲染器中相应RenderCopy()的函数是SW_RenderCopy()。它的源码例如以下所看到的(位于render\software\SDL_render_sw.c)。

static int SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_FRect * dstrect)
{
SDL_Surface *surface = SW_ActivateRenderer(renderer);
SDL_Surface *src = (SDL_Surface *) texture->driverdata;
SDL_Rect final_rect; if (!surface) {
return -1;
} if (renderer->viewport.x || renderer->viewport.y) {
final_rect.x = (int)(renderer->viewport.x + dstrect->x);
final_rect.y = (int)(renderer->viewport.y + dstrect->y);
} else {
final_rect.x = (int)dstrect->x;
final_rect.y = (int)dstrect->y;
}
final_rect.w = (int)dstrect->w;
final_rect.h = (int)dstrect->h; if ( srcrect->w == final_rect.w && srcrect->h == final_rect.h ) {
return SDL_BlitSurface(src, srcrect, surface, &final_rect);
} else {
return SDL_BlitScaled(src, srcrect, surface, &final_rect);
}
}

该函数的源码还没有具体分析。

SDL2源码分析6:拷贝到渲染器(SDL_RenderCopy())的更多相关文章

  1. 一步步实现windows版ijkplayer系列文章之六——SDL2源码分析之OpenGL ES在windows上的渲染过程

    一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...

  2. SDL2源码分析8:视频显示总结

    ===================================================== SDL源码分析系列文章列表: SDL2源码分析1:初始化(SDL_Init()) SDL2源 ...

  3. SDL2源码分析5:更新纹理(SDL_UpdateTexture())

    ===================================================== SDL源码分析系列文章列表: SDL2源码分析1:初始化(SDL_Init()) SDL2源 ...

  4. SDL2源码分析2:窗体(SDL_Window)

    ===================================================== SDL源码分析系列文章列表: SDL2源码分析1:初始化(SDL_Init()) SDL2源 ...

  5. SDL2源码分析1:初始化(SDL_Init())

    ===================================================== SDL源码分析系列文章列表: SDL2源码分析1:初始化(SDL_Init()) SDL2源 ...

  6. springMVC源码分析--HandlerMethodReturnValueHandlerComposite返回值解析器集合(二)

    在上一篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)我们介绍了返回值解析器HandlerMethodReturnValueHand ...

  7. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  8. Tomcat源码分析(类加载与类加载器)

    Tomcat的挑战 Tomcat上可以部署多个项目 Tomcat的一般部署,可以通过多种方式启动一个Tomcat部署多个项目,那么Tomcat在设计时会遇到什么挑战呢? Tomcat运行时需要加载哪些 ...

  9. 2. SOFAJRaft源码分析—JRaft的定时任务调度器是怎么做的?

    看完这个实现之后,感觉还是要多看源码,多研究.其实JRaft的定时任务调度器是基于Netty的时间轮来做的,如果没有看过Netty的源码,很可能并不知道时间轮算法,也就很难想到要去使用这么优秀的定时调 ...

随机推荐

  1. logback自定义格式转换器

    创建自定义格式转换符有两步. 首先,必须继承ClassicConverter类.ClassicConverter对象负责从ILoggingEvent 提取信息,并产生一个字符串.例如,LoggerCo ...

  2. java 利用java运行时的方法得到当前屏幕截图的方法(转)

    将截屏图片保存到本地路径: package com.test; import java.awt.AWTException; import java.awt.Dimension; import java ...

  3. 基于PCA的人脸识别步骤

    代码下载:基于PCA(主成分分析)的人脸识别 人脸识别是一个有监督学习过程,首先利用训练集构造一个人脸模型,然后将测试集与训练集进行匹配,找到与之对应的训练集头像.最容易的方式是直接利用欧式距离计算测 ...

  4. FLASH ROM与EEPROM的区别

    EEPROM,虽然也叫“非易失性数据存储器”,但它不能直接参与ALU运算,只是用于掉电不丢失的数据存储. EEPROM和片内RAM 类似,也属于数据存储器,它的特点是数据掉电可保持,而程序存储器一般指 ...

  5. POJ3264——Balanced Lineup(线段树)

    本文出自:http://blog.csdn.net/svitter 题意:在1~200,000个数中.取一段区间.然后在区间中找出最大的数和最小的数字.求这两个数字的差. 分析:按区间取值,非常明显使 ...

  6. Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  7. [NOIP 2005]-- 篝火晚会

    额~~,对这组题感兴趣的具体的解题报告可以戳戳这里:http://wenku.baidu.com/view/878beb64783e0912a2162aa7.html?qq-pf-to=pcqq.c2 ...

  8. 使用Atlas实现MySQL读写分离+MySQL-(Master-Slave)配置

    参考博文: MySQL-(Master-Slave)配置  本人按照博友北在北方的配置已成功  我使用的是 mysql5.6.27版本. 使用Atlas实现MySQL读写分离 数据切分——Atlas读 ...

  9. 将树莓派变成一个web服务器(2):Nginx+Flask+uWSGI部署全过程

    1)安装Flask,uwsgi,nginx sudo apt-get update sudo apt-get install python-flask #Flask sudo apt-get inst ...

  10. POJ1985 DFS【STL__vector_的应用】

    vector     向量 相当于一个数组    在内存中分配一块连续的内存空间进行存储.支持不指定vector大小的存储.STL内部实现时,首先分配一个非常大的内存空间预备进行存储,即capacit ...