=====================================================
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. Direct3DDirect3D 渲染器中對應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”。