SDL2源代码分析4:纹理(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的内部结构的。有关它的定义在头文件中只有一行代码,如下所示。

[cpp] view plaincopy

  1. /**
  2. *  \brief An efficient driver-specific representation of pixel data
  3. */
  4. struct SDL_Texture;
  5. typedef struct SDL_Texture SDL_Texture;

在源代码工程中可以看到SDL_Texture的定义,位于render\SDL_sysrender.h文件中。它的定义如下。

[cpp] view plaincopy

  1. /* Define the SDL texture structure */
  2. struct SDL_Texture
  3. {
  4. const void *magic;
  5. Uint32 format;              /**< The pixel format of the texture */
  6. int access;                 /**< SDL_TextureAccess */
  7. int w;                      /**< The width of the texture */
  8. int h;                      /**< The height of the texture */
  9. int modMode;                /**< The texture modulation mode */
  10. SDL_BlendMode blendMode;    /**< The texture blend mode */
  11. Uint8 r, g, b, a;           /**< Texture modulation values */
  12. SDL_Renderer *renderer;
  13. /* Support for formats not supported directly by the renderer */
  14. SDL_Texture *native;
  15. SDL_SW_YUVTexture *yuv;
  16. void *pixels;
  17. int pitch;
  18. SDL_Rect locked_rect;
  19. void *driverdata;           /**< Driver specific texture representation */
  20. SDL_Texture *prev;
  21. SDL_Texture *next;
  22. };

可以看出其中包含了一个“纹理”所具备的各种属性。下面来看看如何创建这个SDL_Texture。

SDL_CreateTexture()

函数简介

使用SDL_CreateTexture()基于渲染器创建一个纹理。SDL_CreateTexture()的定义如下。

[cpp] view plaincopy

  1. SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer,
  2. Uint32 format,
  3. int access, int w,
  4. 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中。如下所示。

[cpp] view plaincopy

  1. SDL_Texture * SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
  2. {
  3. SDL_Texture *texture;
  4. CHECK_RENDERER_MAGIC(renderer, NULL);
  5. if (!format) {
  6. format = renderer->info.texture_formats[0];
  7. }
  8. if (SDL_ISPIXELFORMAT_INDEXED(format)) {
  9. SDL_SetError("Palettized textures are not supported");
  10. return NULL;
  11. }
  12. if (w <= 0 || h <= 0) {
  13. SDL_SetError("Texture dimensions can‘t be 0");
  14. return NULL;
  15. }
  16. if ((renderer->info.max_texture_width && w > renderer->info.max_texture_width) ||
  17. (renderer->info.max_texture_height && h > renderer->info.max_texture_height)) {
  18. SDL_SetError("Texture dimensions are limited to %dx%d", renderer->info.max_texture_width, renderer->info.max_texture_height);
  19. return NULL;
  20. }
  21. texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture));
  22. if (!texture) {
  23. SDL_OutOfMemory();
  24. return NULL;
  25. }
  26. texture->magic = &texture_magic;
  27. texture->format = format;
  28. texture->access = access;
  29. texture->w = w;
  30. texture->h = h;
  31. texture->r = 255;
  32. texture->g = 255;
  33. texture->b = 255;
  34. texture->a = 255;
  35. texture->renderer = renderer;
  36. texture->next = renderer->textures;
  37. if (renderer->textures) {
  38. renderer->textures->prev = texture;
  39. }
  40. renderer->textures = texture;
  41. if (IsSupportedFormat(renderer, format)) {
  42. if (renderer->CreateTexture(renderer, texture) < 0) {
  43. SDL_DestroyTexture(texture);
  44. return 0;
  45. }
  46. } else {
  47. texture->native = SDL_CreateTexture(renderer,
  48. GetClosestSupportedFormat(renderer, format),
  49. access, w, h);
  50. if (!texture->native) {
  51. SDL_DestroyTexture(texture);
  52. return NULL;
  53. }
  54. /* Swap textures to have texture before texture->native in the list */
  55. texture->native->next = texture->next;
  56. if (texture->native->next) {
  57. texture->native->next->prev = texture->native;
  58. }
  59. texture->prev = texture->native->prev;
  60. if (texture->prev) {
  61. texture->prev->next = texture;
  62. }
  63. texture->native->prev = texture;
  64. texture->next = texture->native;
  65. renderer->textures = texture;
  66. if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
  67. texture->yuv = SDL_SW_CreateYUVTexture(format, w, h);
  68. if (!texture->yuv) {
  69. SDL_DestroyTexture(texture);
  70. return NULL;
  71. }
  72. } else if (access == SDL_TEXTUREACCESS_STREAMING) {
  73. /* The pitch is 4 byte aligned */
  74. texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3);
  75. texture->pixels = SDL_calloc(1, texture->pitch * h);
  76. if (!texture->pixels) {
  77. SDL_DestroyTexture(texture);
  78. return NULL;
  79. }
  80. }
  81. }
  82. return texture;
  83. }

从源代码中可以看出,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)。

[cpp] view plaincopy

  1. static int D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
  2. {
  3. D3D_RenderData *renderdata = (D3D_RenderData *) renderer->driverdata;
  4. D3D_TextureData *data;
  5. D3DPOOL pool;
  6. DWORD usage;
  7. HRESULT result;
  8. data = (D3D_TextureData *) SDL_calloc(1, sizeof(*data));
  9. if (!data) {
  10. return SDL_OutOfMemory();
  11. }
  12. data->scaleMode = GetScaleQuality();
  13. texture->driverdata = data;
  14. #ifdef USE_DYNAMIC_TEXTURE
  15. if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  16. pool = D3DPOOL_DEFAULT;
  17. usage = D3DUSAGE_DYNAMIC;
  18. } else
  19. #endif
  20. if (texture->access == SDL_TEXTUREACCESS_TARGET) {
  21. /* D3DPOOL_MANAGED does not work with D3DUSAGE_RENDERTARGET */
  22. pool = D3DPOOL_DEFAULT;
  23. usage = D3DUSAGE_RENDERTARGET;
  24. } else {
  25. pool = D3DPOOL_MANAGED;
  26. usage = 0;
  27. }
  28. result =
  29. IDirect3DDevice9_CreateTexture(renderdata->device, texture->w,
  30. texture->h, 1, usage,
  31. PixelFormatToD3DFMT(texture->format),
  32. pool, &data->texture, NULL);
  33. if (FAILED(result)) {
  34. return D3D_SetError("CreateTexture()", result);
  35. }
  36. if (texture->format == SDL_PIXELFORMAT_YV12 ||
  37. texture->format == SDL_PIXELFORMAT_IYUV) {
  38. data->yuv = SDL_TRUE;
  39. result =
  40. IDirect3DDevice9_CreateTexture(renderdata->device, texture->w / 2,
  41. texture->h / 2, 1, usage,
  42. PixelFormatToD3DFMT(texture->format),
  43. pool, &data->utexture, NULL);
  44. if (FAILED(result)) {
  45. return D3D_SetError("CreateTexture()", result);
  46. }
  47. result =
  48. IDirect3DDevice9_CreateTexture(renderdata->device, texture->w / 2,
  49. texture->h / 2, 1, usage,
  50. PixelFormatToD3DFMT(texture->format),
  51. pool, &data->vtexture, NULL);
  52. if (FAILED(result)) {
  53. return D3D_SetError("CreateTexture()", result);
  54. }
  55. }
  56. return 0;
  57. }

从代码中可以看出,该函数调用了Direct3D的API函数IDirect3DDevice9_CreateTexture()创建了一个纹理。

2. OpenGL

OpenGL渲染器中对应CreateTexture()的函数是GL_CreateTexture (),它的源代码如下所示(位于render\opengl\SDL_render_gl.c)。

[cpp] view plaincopy

  1. static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
  2. {
  3. GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
  4. GL_TextureData *data;
  5. GLint internalFormat;
  6. GLenum format, type;
  7. int texture_w, texture_h;
  8. GLenum scaleMode;
  9. GL_ActivateRenderer(renderer);
  10. if (!convert_format(renderdata, texture->format, &internalFormat,
  11. &format, &type)) {
  12. return SDL_SetError("Texture format %s not supported by OpenGL",
  13. SDL_GetPixelFormatName(texture->format));
  14. }
  15. data = (GL_TextureData *) SDL_calloc(1, sizeof(*data));
  16. if (!data) {
  17. return SDL_OutOfMemory();
  18. }
  19. if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  20. size_t size;
  21. data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
  22. size = texture->h * data->pitch;
  23. if (texture->format == SDL_PIXELFORMAT_YV12 ||
  24. texture->format == SDL_PIXELFORMAT_IYUV) {
  25. /* Need to add size for the U and V planes */
  26. size += (2 * (texture->h * data->pitch) / 4);
  27. }
  28. data->pixels = SDL_calloc(1, size);
  29. if (!data->pixels) {
  30. SDL_free(data);
  31. return SDL_OutOfMemory();
  32. }
  33. }
  34. if (texture->access == SDL_TEXTUREACCESS_TARGET) {
  35. data->fbo = GL_GetFBO(renderdata, texture->w, texture->h);
  36. } else {
  37. data->fbo = NULL;
  38. }
  39. GL_CheckError("", renderer);
  40. renderdata->glGenTextures(1, &data->texture);
  41. if (GL_CheckError("glGenTexures()", renderer) < 0) {
  42. SDL_free(data);
  43. return -1;
  44. }
  45. texture->driverdata = data;
  46. if ((renderdata->GL_ARB_texture_rectangle_supported)
  47. /* && texture->access != SDL_TEXTUREACCESS_TARGET */){
  48. data->type = GL_TEXTURE_RECTANGLE_ARB;
  49. texture_w = texture->w;
  50. texture_h = texture->h;
  51. data->texw = (GLfloat) texture_w;
  52. data->texh = (GLfloat) texture_h;
  53. } else {
  54. data->type = GL_TEXTURE_2D;
  55. texture_w = power_of_2(texture->w);
  56. texture_h = power_of_2(texture->h);
  57. data->texw = (GLfloat) (texture->w) / texture_w;
  58. data->texh = (GLfloat) texture->h / texture_h;
  59. }
  60. data->format = format;
  61. data->formattype = type;
  62. scaleMode = GetScaleQuality();
  63. renderdata->glEnable(data->type);
  64. renderdata->glBindTexture(data->type, data->texture);
  65. renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
  66. renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
  67. /* According to the spec, CLAMP_TO_EDGE is the default for TEXTURE_RECTANGLE
  68. and setting it causes an INVALID_ENUM error in the latest NVidia drivers.
  69. */
  70. if (data->type != GL_TEXTURE_RECTANGLE_ARB) {
  71. renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
  72. GL_CLAMP_TO_EDGE);
  73. renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
  74. GL_CLAMP_TO_EDGE);
  75. }
  76. #ifdef __MACOSX__
  77. #ifndef GL_TEXTURE_STORAGE_HINT_APPLE
  78. #define GL_TEXTURE_STORAGE_HINT_APPLE       0x85BC
  79. #endif
  80. #ifndef STORAGE_CACHED_APPLE
  81. #define STORAGE_CACHED_APPLE                0x85BE
  82. #endif
  83. #ifndef STORAGE_SHARED_APPLE
  84. #define STORAGE_SHARED_APPLE                0x85BF
  85. #endif
  86. if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  87. renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
  88. GL_STORAGE_SHARED_APPLE);
  89. } else {
  90. renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
  91. GL_STORAGE_CACHED_APPLE);
  92. }
  93. if (texture->access == SDL_TEXTUREACCESS_STREAMING
  94. && texture->format == SDL_PIXELFORMAT_ARGB8888
  95. && (texture->w % 8) == 0) {
  96. renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
  97. renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  98. renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,
  99. (data->pitch / SDL_BYTESPERPIXEL(texture->format)));
  100. renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
  101. texture_h, 0, format, type, data->pixels);
  102. renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
  103. }
  104. else
  105. #endif
  106. {
  107. renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
  108. texture_h, 0, format, type, NULL);
  109. }
  110. renderdata->glDisable(data->type);
  111. if (GL_CheckError("glTexImage2D()", renderer) < 0) {
  112. return -1;
  113. }
  114. if (texture->format == SDL_PIXELFORMAT_YV12 ||
  115. texture->format == SDL_PIXELFORMAT_IYUV) {
  116. data->yuv = SDL_TRUE;
  117. renderdata->glGenTextures(1, &data->utexture);
  118. renderdata->glGenTextures(1, &data->vtexture);
  119. renderdata->glEnable(data->type);
  120. renderdata->glBindTexture(data->type, data->utexture);
  121. renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
  122. scaleMode);
  123. renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
  124. scaleMode);
  125. renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
  126. GL_CLAMP_TO_EDGE);
  127. renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
  128. GL_CLAMP_TO_EDGE);
  129. renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
  130. texture_h/2, 0, format, type, NULL);
  131. renderdata->glBindTexture(data->type, data->vtexture);
  132. renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
  133. scaleMode);
  134. renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
  135. scaleMode);
  136. renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
  137. GL_CLAMP_TO_EDGE);
  138. renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
  139. GL_CLAMP_TO_EDGE);
  140. renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
  141. texture_h/2, 0, format, type, NULL);
  142. renderdata->glDisable(data->type);
  143. }
  144. return GL_CheckError("", renderer);
  145. }

从代码中可以看出,该函数调用了OpenGL的API函数glGenTextures(),glBindTexture()创建了一个纹理。并且使用glTexParameteri()设置了有关的一些参数。

在这里有一点需要注意,在OpenGL渲染器中,如果输入像素格式是YV12或者IYUV,就会使用3个纹理。

3. Software

Software渲染器中对应CreateTexture()的函数是SW_CreateTexture (),它的源代码如下所示(位于render\software\SDL_render_sw.c)。

[cpp] view plaincopy

  1. static int SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
  2. {
  3. int bpp;
  4. Uint32 Rmask, Gmask, Bmask, Amask;
  5. if (!SDL_PixelFormatEnumToMasks
  6. (texture->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
  7. return SDL_SetError("Unknown texture format");
  8. }
  9. texture->driverdata =
  10. SDL_CreateRGBSurface(0, texture->w, texture->h, bpp, Rmask, Gmask,
  11. Bmask, Amask);
  12. SDL_SetSurfaceColorMod(texture->driverdata, texture->r, texture->g,
  13. texture->b);
  14. SDL_SetSurfaceAlphaMod(texture->driverdata, texture->a);
  15. SDL_SetSurfaceBlendMode(texture->driverdata, texture->blendMode);
  16. if (texture->access == SDL_TEXTUREACCESS_STATIC) {
  17. SDL_SetSurfaceRLE(texture->driverdata, 1);
  18. }
  19. if (!texture->driverdata) {
  20. return -1;
  21. }
  22. return 0;
  23. }

该函数的源代码还没有详细分析。可以看出其中调用了SDL_CreateRGBSurface()创建了“Surface”。

时间: 2024-08-05 07:05:39

SDL2源代码分析4:纹理(SDL_Texture)的相关文章

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

上一篇文章分析了SDL的创建纹理函数SDL_CreateTexture().这篇文章继续分析SDL的源代码.本文分析SDL更新纹理数据函数SDL_UpdateTexture(). SDL播放视频的代码流程如下所示.初始化:  SDL_Init(): 初始化SDL. SDL_CreateWindow(): 创建窗口(Window). SDL_CreateRenderer(): 基于窗口创建渲染器(Render). SDL_CreateTexture(): 创建纹理(Texture). 循环渲染数据

转:SDL2源代码分析

1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中已经叙述过了,不再重复.这两篇文章中也提到了一张SDL的原理图,如下所示: 从这个图中可以看出,SDL根据系统的不同调用不同的API完成相应的功能.至于它是如何实现的,将会在后文中详细叙述.下面不再罗嗦,直接进入正题. 使用SDL播放一个视频代码流程大体如下 初始化: SDL_Init(): 初始化SDL. 

SDL2源代码分析6:复制到渲染器(SDL_RenderCopy())

上一篇文章分析了SDL更新纹理像素数据的函数SDL_UpdateTexture().这篇文章继续分析SDL的源代码.本文分析SDL纹理复制到渲染目标的函数SDL_RenderCopy(). SDL播放视频的代码流程如下所示.初始化:  SDL_Init(): 初始化SDL. SDL_CreateWindow(): 创建窗口(Window). SDL_CreateRenderer(): 基于窗口创建渲染器(Render). SDL_CreateTexture(): 创建纹理(Texture). 循

SDL2源代码分析2:窗口(SDL_Window)

上一篇文章分析了SDL的初始化函数SDL_Init().这篇文章继续分析SDL的源代码.本文分析SDL的窗口(Window). SDL播放视频的代码流程如下所示. 初始化:  SDL_Init(): 初始化SDL. SDL_CreateWindow(): 创建窗口(Window). SDL_CreateRenderer(): 基于窗口创建渲染器(Render). SDL_CreateTexture(): 创建纹理(Texture). 循环渲染数据:  SDL_UpdateTexture(): 设

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

上一篇文章分析了SDL纹理赋值给渲染目标的函数SDL_RenderCopy().这篇文章分析SDL显示视频最后的一个函数:SDL_RenderPresent(). SDL播放视频的代码流程如下所示.初始化:  SDL_Init(): 初始化SDL. SDL_CreateWindow(): 创建窗口(Window). SDL_CreateRenderer(): 基于窗口创建渲染器(Render). SDL_CreateTexture(): 创建纹理(Texture). 循环渲染数据:  SDL_U

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

本文简单总结一下SDL显示视频的源代码. SDL显示视频的结构体 SDL显示视频涉及到下列结构体: SDL_Window:代表了窗口SDL_Renderer:代表了渲染器SDL_Texture:代表了纹理SDL_Rect:一个矩形框,用于确定纹理显示的位置. 上述几个结构体之间的关系如下图所示. PS:该图源自于文章<最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)> 由图可见,YUV/RGB像素数据首先加载至SDL_Texture,然后通过SDL_Render渲染

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

上一篇文章分析了SDL中创建窗口的函数SDL_CreateWindow().这篇文章继续分析SDL的源代码.本文分析SDL的渲染器(SDL_Renderer). SDL播放视频的代码流程如下所示. 初始化:  SDL_Init(): 初始化SDL. SDL_CreateWindow(): 创建窗口(Window). SDL_CreateRenderer(): 基于窗口创建渲染器(Render). SDL_CreateTexture(): 创建纹理(Texture). 循环渲染数据:  SDL_U

SDL2源代码分析1:初始化

打算花一段时间研究一下SDL的内部代码.前面几篇文章<最简单的视音频播放示例1:总述>中记录了视频.音频播放的技术,文中提及了SDL实际上封装了Direct3D,DirectSound这类的底层API.但是SDL究竟是如何封装的呢?这次打算深入其源代码一探究竟,看看它是如何封装这些API的. SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中已经叙述过了,不再重复.这两篇文章中也提

SDL2来源分析3:渲染(SDL_Renderer)

===================================================== 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