SDL2源代碼分析5:更新紋理(SDL_UpdateTexture())

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

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_CreateTexture()。這篇文章繼續分析SDL的源代碼。本文分析SDL更新紋理數據函數SDL_UpdateTexture()。



SDL播放視頻的代碼流程如下所示。
初始化: 
SDL_Init(): 初始化SDL。 
SDL_CreateWindow(): 創建窗口(Window)。 
SDL_CreateRenderer(): 基於窗口創建渲染器(Render)。 
SDL_CreateTexture(): 創建紋理(Texture)。 
循環渲染數據: 
SDL_UpdateTexture(): 設置紋理的數據。 
SDL_RenderCopy(): 紋理複製給渲染器。 
SDL_RenderPresent(): 顯示。
上篇文章分析了該流程中的第4個函數SDL_CreateTexture()。本文繼續分析該流程中的第5個函數SDL_UpdateTexture()。

SDL_UpdateTexture()

函數簡介

SDL使用SDL_UpdateTexture()設置紋理的像素數據。SDL_UpdateTexture()的原型如下。
  1. int SDLCALL SDL_UpdateTexture(SDL_Texture * texture,  
  2.                                           const SDL_Rect * rect,  
  3.                                               const void *pixels, int pitch);  

參數的含義如下。

texture:目標紋理。

rect:更新像素的矩形區域。設置爲NULL的時候更新整個區域。

pixels:像素數據。

pitch:一行像素數據的字節數。

成功的話返回0,失敗的話返回-1。


函數調用關係圖

SDL_UpdateTexture()關鍵函數的調用關係可以用下圖表示。

上面的圖片不太清晰,更清晰的圖片上傳到了相冊裏面:

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

把相冊裏面的圖片保存下來就可以得到清晰的圖片。

源代碼分析

SDL_UpdateTexture()的源代碼位於render\SDL_render.c中。如下所示。

  1. int SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,  
  2.                   const void *pixels, int pitch)  
  3. {  
  4.     SDL_Renderer *renderer;  
  5.     SDL_Rect full_rect;  
  6.   
  7.   
  8.     CHECK_TEXTURE_MAGIC(texture, -1);  
  9.   
  10.   
  11.     if (!pixels) {  
  12.         return SDL_InvalidParamError("pixels");  
  13.     }  
  14.     if (!pitch) {  
  15.         return SDL_InvalidParamError("pitch");  
  16.     }  
  17.   
  18.   
  19.     if (!rect) {  
  20.         full_rect.x = 0;  
  21.         full_rect.y = 0;  
  22.         full_rect.w = texture->w;  
  23.         full_rect.h = texture->h;  
  24.         rect = &full_rect;  
  25.     }  
  26.   
  27.   
  28.     if (texture->yuv) {  
  29.         return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);  
  30.     } else if (texture->native) {  
  31.         return SDL_UpdateTextureNative(texture, rect, pixels, pitch);  
  32.     } else {  
  33.         renderer = texture->renderer;  
  34.         return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);  
  35.     }  
  36. }  

從源代碼中可以看出,SDL_UpdateTexture()的大致流程如下。
1. 檢查輸入參數的合理性。例如像素格式是否支持,寬和高是否小於等於0等等。
2. 如果是一些特殊的格式,進行一定的處理:
a) 如果輸入的像素數據是YUV格式的,則會調用SDL_UpdateTextureYUV()進行處理。
b) 如果輸入的像素數據的像素格式不是渲染器支持的格式,則會調用SDL_UpdateTextureNative()進行處理。
3. 調用SDL_Render的UpdateTexture()方法更新紋理。這一步是整個函數的核心。
下面我們詳細看一下幾種不同的渲染器的UpdateTexture ()的方法。

1. Direct3D

Direct3D 渲染器中對應UpdateTexture ()的函數是D3D_UpdateTexture(),它的源代碼如下所示(位於render\direct3d\SDL_render_d3d.c)。
  1. static int  
  2. D3D_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,  
  3.                   const SDL_Rect * rect, const void *pixels, int pitch)  
  4. {  
  5.     D3D_TextureData *data = (D3D_TextureData *) texture->driverdata;  
  6.     SDL_bool full_texture = SDL_FALSE;  
  7.   
  8.   
  9. #ifdef USE_DYNAMIC_TEXTURE  
  10.     if (texture->access == SDL_TEXTUREACCESS_STREAMING &&  
  11.         rect->x == 0 && rect->y == 0 &&  
  12.         rect->w == texture->w && rect->h == texture->h) {  
  13.         full_texture = SDL_TRUE;  
  14.     }  
  15. #endif  
  16.   
  17.   
  18.     if (!data) {  
  19.         SDL_SetError("Texture is not currently available");  
  20.         return -1;  
  21.     }  
  22.   
  23.   
  24.     if (D3D_UpdateTextureInternal(data->texture, texture->format, full_texture, rect->x, rect->y, rect->w, rect->h, pixels, pitch) < 0) {  
  25.         return -1;  
  26.     }  
  27.   
  28.   
  29.     if (data->yuv) {  
  30.         /* Skip to the correct offset into the next texture */  
  31.         pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);  
  32.   
  33.   
  34.         if (D3D_UpdateTextureInternal(texture->format == SDL_PIXELFORMAT_YV12 ? data->vtexture : data->utexture, texture->format, full_texture, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, pixels, pitch / 2) < 0) {  
  35.             return -1;  
  36.         }  
  37.   
  38.   
  39.         /* Skip to the correct offset into the next texture */  
  40.         pixels = (const void*)((const Uint8*)pixels + (rect->h * pitch)/4);  
  41.         if (D3D_UpdateTextureInternal(texture->format == SDL_PIXELFORMAT_YV12 ? data->utexture : data->vtexture, texture->format, full_texture, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, pixels, pitch / 2) < 0) {  
  42.             return -1;  
  43.         }  
  44.     }  
  45.     return 0;  
  46. }  


從代碼中可以看出,該函數調用了D3D_UpdateTextureInternal()函數。在這裏需要注意,如果輸入像素格式是YUV,就會使用3個紋理,對於多出的那2個紋理會單獨進行處理。調用的函數D3D_UpdateTextureInternal()代碼如下。

  1. static int D3D_UpdateTextureInternal(IDirect3DTexture9 *texture, Uint32 format, SDL_bool full_texture, int x, int y, int w, int h, const void *pixels, int pitch)  
  2. {  
  3.     RECT d3drect;  
  4.     D3DLOCKED_RECT locked;  
  5.     const Uint8 *src;  
  6.     Uint8 *dst;  
  7.     int row, length;  
  8.     HRESULT result;  
  9.   
  10.   
  11.     if (full_texture) {  
  12.         result = IDirect3DTexture9_LockRect(texture, 0, &locked, NULL, D3DLOCK_DISCARD);  
  13.     } else {  
  14.         d3drect.left = x;  
  15.         d3drect.right = x + w;  
  16.         d3drect.top = y;  
  17.         d3drect.bottom = y + h;  
  18.         result = IDirect3DTexture9_LockRect(texture, 0, &locked, &d3drect, 0);  
  19.     }  
  20.   
  21.   
  22.     if (FAILED(result)) {  
  23.         return D3D_SetError("LockRect()", result);  
  24.     }  
  25.   
  26.   
  27.     src = (const Uint8 *)pixels;  
  28.     dst = locked.pBits;  
  29.     length = w * SDL_BYTESPERPIXEL(format);  
  30.     if (length == pitch && length == locked.Pitch) {  
  31.         SDL_memcpy(dst, src, length*h);  
  32.     } else {  
  33.         if (length > pitch) {  
  34.             length = pitch;  
  35.         }  
  36.         if (length > locked.Pitch) {  
  37.             length = locked.Pitch;  
  38.         }  
  39.         for (row = 0; row < h; ++row) {  
  40.             SDL_memcpy(dst, src, length);  
  41.             src += pitch;  
  42.             dst += locked.Pitch;  
  43.         }  
  44.     }  
  45.     IDirect3DTexture9_UnlockRect(texture, 0);  
  46.   
  47.   
  48.     return 0;  
  49. }  


從代碼中可以看出,該函數首先調用IDirect3DTexture9_LockRect()鎖定紋理,然後使用SDL_memcpy()將新的像素數據拷貝至紋理(SDL_memcpy()實際上就是memcpy()), 最後使用IDirect3DTexture9_UnlockRect()解鎖紋理。

2. OpenGL

OpenGL渲染器中對應UpdateTexture()的函數是GL_UpdateTexture(),它的源代碼如下所示(位於render\opengl\SDL_render_gl.c)。
  1. static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,  
  2.                  const SDL_Rect * rect, const void *pixels, int pitch)  
  3. {  
  4.     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;  
  5.     GL_TextureData *data = (GL_TextureData *) texture->driverdata;  
  6.   
  7.   
  8.     GL_ActivateRenderer(renderer);  
  9.   
  10.   
  11.     renderdata->glEnable(data->type);  
  12.     renderdata->glBindTexture(data->type, data->texture);  
  13.     renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);  
  14.     renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,  
  15.                               (pitch / SDL_BYTESPERPIXEL(texture->format)));  
  16.     renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,  
  17.                                 rect->h, data->format, data->formattype,  
  18.                                 pixels);  
  19.     if (data->yuv) {  
  20.         renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / 2));  
  21.   
  22.   
  23.         /* Skip to the correct offset into the next texture */  
  24.         pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);  
  25.         if (texture->format == SDL_PIXELFORMAT_YV12) {  
  26.             renderdata->glBindTexture(data->type, data->vtexture);  
  27.         } else {  
  28.             renderdata->glBindTexture(data->type, data->utexture);  
  29.         }  
  30.         renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,  
  31.                                     rect->w/2, rect->h/2,  
  32.                                     data->format, data->formattype, pixels);  
  33.   
  34.   
  35.         /* Skip to the correct offset into the next texture */  
  36.         pixels = (const void*)((const Uint8*)pixels + (rect->h * pitch)/4);  
  37.         if (texture->format == SDL_PIXELFORMAT_YV12) {  
  38.             renderdata->glBindTexture(data->type, data->utexture);  
  39.         } else {  
  40.             renderdata->glBindTexture(data->type, data->vtexture);  
  41.         }  
  42.         renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,  
  43.                                     rect->w/2, rect->h/2,  
  44.                                     data->format, data->formattype, pixels);  
  45.     }  
  46.     renderdata->glDisable(data->type);  
  47.   
  48.   
  49.     return GL_CheckError("glTexSubImage2D()", renderer);  
  50. }  

從代碼中可以看出,該函數調用了OpenGL的API函數glBindTexture (),glTexSubImage2D()等更新了一個紋理。
在這裏有一點需要注意,如果輸入像素格式是YUV,就會使用3個紋理,對於多出的那2個紋理會單獨進行處理。

3. Software

Software渲染器中對應UpdateTexture()的函數是SW_UpdateTexture(),它的源代碼如下所示(位於render\software\SDL_render_sw.c)。
  1. static int SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,  
  2.                  const SDL_Rect * rect, const void *pixels, int pitch)  
  3. {  
  4.     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;  
  5.     Uint8 *src, *dst;  
  6.     int row;  
  7.     size_t length;  
  8.   
  9.   
  10.     if(SDL_MUSTLOCK(surface))  
  11.         SDL_LockSurface(surface);  
  12.     src = (Uint8 *) pixels;  
  13.     dst = (Uint8 *) surface->pixels +  
  14.                         rect->y * surface->pitch +  
  15.                         rect->x * surface->format->BytesPerPixel;  
  16.     length = rect->w * surface->format->BytesPerPixel;  
  17.     for (row = 0; row < rect->h; ++row) {  
  18.         SDL_memcpy(dst, src, length);  
  19.         src += pitch;  
  20.         dst += surface->pitch;  
  21.     }  
  22.     if(SDL_MUSTLOCK(surface))  
  23.         SDL_UnlockSurface(surface);  
  24.     return 0;  
  25. }  

該函數的源代碼還沒有詳細分析。其中最關鍵的函數要數SDL_memcpy()了,正是這個函數更新了紋理的像素數據。但是Software渲染器紋理修改的時候是否需要Lock()和Unlock()呢?這一點一直也沒太搞清。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章