=====================================================
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_CreateRenderer()。這篇文章繼續分析SDL的源代碼。本文分析SDL的紋理(SDL_Texture)。
SDL播放視訊的代碼流程如下所示。
初始化:
SDL_Init(): 初始化SDL。
SDL_CreateWindow(): 建立視窗(Window)。
SDL_CreateRenderer(): 基于視窗建立渲染器(Render)。
SDL_CreateTexture(): 建立紋理(Texture)。
循環渲染資料:
SDL_UpdateTexture(): 設定紋理的資料。
SDL_RenderCopy(): 紋理複制給渲染器。
SDL_RenderPresent(): 顯示。
上篇文章分析了該流程中的第3個函數SDL_CreateRenderer()。本文繼續分析該流程中的第4個函數SDL_CreateTexture()。
SDL_Texture
SDL_Texture結構定義了一個SDL中的紋理。如果直接使用SDL2編譯好的SDK的話,是看不到SDL_Texture的内部結構的。有關它的定義在頭檔案中隻有一行代碼,如下所示。
/**
* \brief An efficient driver-specific representation of pixel data
*/
struct SDL_Texture;
typedef struct SDL_Texture SDL_Texture;
在源代碼工程中可以看到SDL_Texture的定義,位于render\SDL_sysrender.h檔案中。它的定義如下。
/* Define the SDL texture structure */
struct SDL_Texture
{
const void *magic;
Uint32 format; /**< The pixel format of the texture */
int access; /**< SDL_TextureAccess */
int w; /**< The width of the texture */
int h; /**< The height of the texture */
int modMode; /**< The texture modulation mode */
SDL_BlendMode blendMode; /**< The texture blend mode */
Uint8 r, g, b, a; /**< Texture modulation values */
SDL_Renderer *renderer;
/* Support for formats not supported directly by the renderer */
SDL_Texture *native;
SDL_SW_YUVTexture *yuv;
void *pixels;
int pitch;
SDL_Rect locked_rect;
void *driverdata; /**< Driver specific texture representation */
SDL_Texture *prev;
SDL_Texture *next;
};
可以看出其中包含了一個“紋理”所具備的各種屬性。下面來看看如何建立這個SDL_Texture。
SDL_CreateTexture()
函數簡介
使用SDL_CreateTexture()基于渲染器建立一個紋理。SDL_CreateTexture()的原型如下。
SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer,
Uint32 format,
int access, int w,
int h);
參數的含義如下。
renderer:目标渲染器。
format :紋理的格式。後面會詳述。
access :可以取以下值(定義位于SDL_TextureAccess中)
SDL_TEXTUREACCESS_STATIC :變化極少
SDL_TEXTUREACCESS_STREAMING :變化頻繁
SDL_TEXTUREACCESS_TARGET :暫時沒有了解
w :紋理的寬
h :紋理的高
建立成功則傳回紋理的ID,失敗傳回0。
函數調用關系圖
SDL_ CreateTexture ()關鍵函數的調用關系可以用下圖表示。
上面的圖檔不太清晰,更清晰的圖檔上傳到了相冊裡面:
http://my.csdn.net/leixiaohua1020/album/detail/1793543
把相冊裡面的圖檔儲存下來就可以得到清晰的圖檔了。
源代碼分析
SDL_CreateTexture()的源代碼位于render\SDL_render.c中。如下所示。
SDL_Texture * SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
{
SDL_Texture *texture;
CHECK_RENDERER_MAGIC(renderer, NULL);
if (!format) {
format = renderer->info.texture_formats[0];
}
if (SDL_ISPIXELFORMAT_INDEXED(format)) {
SDL_SetError("Palettized textures are not supported");
return NULL;
}
if (w <= 0 || h <= 0) {
SDL_SetError("Texture dimensions can't be 0");
return NULL;
}
if ((renderer->info.max_texture_width && w > renderer->info.max_texture_width) ||
(renderer->info.max_texture_height && h > renderer->info.max_texture_height)) {
SDL_SetError("Texture dimensions are limited to %dx%d", renderer->info.max_texture_width, renderer->info.max_texture_height);
return NULL;
}
texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture));
if (!texture) {
SDL_OutOfMemory();
return NULL;
}
texture->magic = &texture_magic;
texture->format = format;
texture->access = access;
texture->w = w;
texture->h = h;
texture->r = 255;
texture->g = 255;
texture->b = 255;
texture->a = 255;
texture->renderer = renderer;
texture->next = renderer->textures;
if (renderer->textures) {
renderer->textures->prev = texture;
}
renderer->textures = texture;
if (IsSupportedFormat(renderer, format)) {
if (renderer->CreateTexture(renderer, texture) < 0) {
SDL_DestroyTexture(texture);
return 0;
}
} else {
texture->native = SDL_CreateTexture(renderer,
GetClosestSupportedFormat(renderer, format),
access, w, h);
if (!texture->native) {
SDL_DestroyTexture(texture);
return NULL;
}
/* Swap textures to have texture before texture->native in the list */
texture->native->next = texture->next;
if (texture->native->next) {
texture->native->next->prev = texture->native;
}
texture->prev = texture->native->prev;
if (texture->prev) {
texture->prev->next = texture;
}
texture->native->prev = texture;
texture->next = texture->native;
renderer->textures = texture;
if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
texture->yuv = SDL_SW_CreateYUVTexture(format, w, h);
if (!texture->yuv) {
SDL_DestroyTexture(texture);
return NULL;
}
} else if (access == SDL_TEXTUREACCESS_STREAMING) {
/* The pitch is 4 byte aligned */
texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3);
texture->pixels = SDL_calloc(1, texture->pitch * h);
if (!texture->pixels) {
SDL_DestroyTexture(texture);
return NULL;
}
}
}
return texture;
}
從源代碼中可以看出,SDL_CreateTexture()的大緻流程如下。
1. 檢查輸入參數的合理性。例如像素格式是否支援,寬和高是否小于等于0等等。
2. 建立一個SDL_Texture。調用SDL_calloc()(實際上就是calloc())為建立的SDL_Texture配置設定記憶體。
3. 調用SDL_Render的CreateTexture()方法建立紋理。這一步是整個函數的核心。
下面我們詳細看一下幾種不同的渲染器的CreateTexture()的方法。
1. Direct3D
Direct3D 渲染器中對應CreateTexture()的函數是D3D_CreateTexture(),它的源代碼如下所示(位于render\direct3d\SDL_render_d3d.c)。
static int D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
D3D_RenderData *renderdata = (D3D_RenderData *) renderer->driverdata;
D3D_TextureData *data;
D3DPOOL pool;
DWORD usage;
HRESULT result;
data = (D3D_TextureData *) SDL_calloc(1, sizeof(*data));
if (!data) {
return SDL_OutOfMemory();
}
data->scaleMode = GetScaleQuality();
texture->driverdata = data;
#ifdef USE_DYNAMIC_TEXTURE
if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
pool = D3DPOOL_DEFAULT;
usage = D3DUSAGE_DYNAMIC;
} else
#endif
if (texture->access == SDL_TEXTUREACCESS_TARGET) {
/* D3DPOOL_MANAGED does not work with D3DUSAGE_RENDERTARGET */
pool = D3DPOOL_DEFAULT;
usage = D3DUSAGE_RENDERTARGET;
} else {
pool = D3DPOOL_MANAGED;
usage = 0;
}
result =
IDirect3DDevice9_CreateTexture(renderdata->device, texture->w,
texture->h, 1, usage,
PixelFormatToD3DFMT(texture->format),
pool, &data->texture, NULL);
if (FAILED(result)) {
return D3D_SetError("CreateTexture()", result);
}
if (texture->format == SDL_PIXELFORMAT_YV12 ||
texture->format == SDL_PIXELFORMAT_IYUV) {
data->yuv = SDL_TRUE;
result =
IDirect3DDevice9_CreateTexture(renderdata->device, texture->w / 2,
texture->h / 2, 1, usage,
PixelFormatToD3DFMT(texture->format),
pool, &data->utexture, NULL);
if (FAILED(result)) {
return D3D_SetError("CreateTexture()", result);
}
result =
IDirect3DDevice9_CreateTexture(renderdata->device, texture->w / 2,
texture->h / 2, 1, usage,
PixelFormatToD3DFMT(texture->format),
pool, &data->vtexture, NULL);
if (FAILED(result)) {
return D3D_SetError("CreateTexture()", result);
}
}
return 0;
}
從代碼中可以看出,該函數調用了Direct3D的API函數IDirect3DDevice9_CreateTexture()建立了一個紋理。
2. OpenGL
OpenGL渲染器中對應CreateTexture()的函數是GL_CreateTexture (),它的源代碼如下所示(位于render\opengl\SDL_render_gl.c)。
static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
GL_TextureData *data;
GLint internalFormat;
GLenum format, type;
int texture_w, texture_h;
GLenum scaleMode;
GL_ActivateRenderer(renderer);
if (!convert_format(renderdata, texture->format, &internalFormat,
&format, &type)) {
return SDL_SetError("Texture format %s not supported by OpenGL",
SDL_GetPixelFormatName(texture->format));
}
data = (GL_TextureData *) SDL_calloc(1, sizeof(*data));
if (!data) {
return SDL_OutOfMemory();
}
if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
size_t size;
data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
size = texture->h * data->pitch;
if (texture->format == SDL_PIXELFORMAT_YV12 ||
texture->format == SDL_PIXELFORMAT_IYUV) {
/* Need to add size for the U and V planes */
size += (2 * (texture->h * data->pitch) / 4);
}
data->pixels = SDL_calloc(1, size);
if (!data->pixels) {
SDL_free(data);
return SDL_OutOfMemory();
}
}
if (texture->access == SDL_TEXTUREACCESS_TARGET) {
data->fbo = GL_GetFBO(renderdata, texture->w, texture->h);
} else {
data->fbo = NULL;
}
GL_CheckError("", renderer);
renderdata->glGenTextures(1, &data->texture);
if (GL_CheckError("glGenTexures()", renderer) < 0) {
SDL_free(data);
return -1;
}
texture->driverdata = data;
if ((renderdata->GL_ARB_texture_rectangle_supported)
/* && texture->access != SDL_TEXTUREACCESS_TARGET */){
data->type = GL_TEXTURE_RECTANGLE_ARB;
texture_w = texture->w;
texture_h = texture->h;
data->texw = (GLfloat) texture_w;
data->texh = (GLfloat) texture_h;
} else {
data->type = GL_TEXTURE_2D;
texture_w = power_of_2(texture->w);
texture_h = power_of_2(texture->h);
data->texw = (GLfloat) (texture->w) / texture_w;
data->texh = (GLfloat) texture->h / texture_h;
}
data->format = format;
data->formattype = type;
scaleMode = GetScaleQuality();
renderdata->glEnable(data->type);
renderdata->glBindTexture(data->type, data->texture);
renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
/* According to the spec, CLAMP_TO_EDGE is the default for TEXTURE_RECTANGLE
and setting it causes an INVALID_ENUM error in the latest NVidia drivers.
*/
if (data->type != GL_TEXTURE_RECTANGLE_ARB) {
renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
}
#ifdef __MACOSX__
#ifndef GL_TEXTURE_STORAGE_HINT_APPLE
#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC
#endif
#ifndef STORAGE_CACHED_APPLE
#define STORAGE_CACHED_APPLE 0x85BE
#endif
#ifndef STORAGE_SHARED_APPLE
#define STORAGE_SHARED_APPLE 0x85BF
#endif
if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
GL_STORAGE_SHARED_APPLE);
} else {
renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
GL_STORAGE_CACHED_APPLE);
}
if (texture->access == SDL_TEXTUREACCESS_STREAMING
&& texture->format == SDL_PIXELFORMAT_ARGB8888
&& (texture->w % 8) == 0) {
renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,
(data->pitch / SDL_BYTESPERPIXEL(texture->format)));
renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
texture_h, 0, format, type, data->pixels);
renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
}
else
#endif
{
renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
texture_h, 0, format, type, NULL);
}
renderdata->glDisable(data->type);
if (GL_CheckError("glTexImage2D()", renderer) < 0) {
return -1;
}
if (texture->format == SDL_PIXELFORMAT_YV12 ||
texture->format == SDL_PIXELFORMAT_IYUV) {
data->yuv = SDL_TRUE;
renderdata->glGenTextures(1, &data->utexture);
renderdata->glGenTextures(1, &data->vtexture);
renderdata->glEnable(data->type);
renderdata->glBindTexture(data->type, data->utexture);
renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
scaleMode);
renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
scaleMode);
renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
texture_h/2, 0, format, type, NULL);
renderdata->glBindTexture(data->type, data->vtexture);
renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
scaleMode);
renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
scaleMode);
renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
texture_h/2, 0, format, type, NULL);
renderdata->glDisable(data->type);
}
return GL_CheckError("", renderer);
}
從代碼中可以看出,該函數調用了OpenGL的API函數glGenTextures(),glBindTexture()建立了一個紋理。并且使用glTexParameteri()設定了有關的一些參數。
在這裡有一點需要注意,在OpenGL渲染器中,如果輸入像素格式是YV12或者IYUV,就會使用3個紋理。
3. Software
Software渲染器中對應CreateTexture()的函數是SW_CreateTexture (),它的源代碼如下所示(位于render\software\SDL_render_sw.c)。
static int SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
int bpp;
Uint32 Rmask, Gmask, Bmask, Amask;
if (!SDL_PixelFormatEnumToMasks
(texture->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
return SDL_SetError("Unknown texture format");
}
texture->driverdata =
SDL_CreateRGBSurface(0, texture->w, texture->h, bpp, Rmask, Gmask,
Bmask, Amask);
SDL_SetSurfaceColorMod(texture->driverdata, texture->r, texture->g,
texture->b);
SDL_SetSurfaceAlphaMod(texture->driverdata, texture->a);
SDL_SetSurfaceBlendMode(texture->driverdata, texture->blendMode);
if (texture->access == SDL_TEXTUREACCESS_STATIC) {
SDL_SetSurfaceRLE(texture->driverdata, 1);
}
if (!texture->driverdata) {
return -1;
}
return 0;
}
該函數的源代碼還沒有詳細分析。可以看出其中調用了SDL_CreateRGBSurface()建立了“Surface”。