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

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. CHECK_TEXTURE_MAGIC(texture, -1);
  8.  
  9. if (!pixels) {
  10. return SDL_InvalidParamError("pixels");
  11. }
  12. if (!pitch) {
  13. return SDL_InvalidParamError("pitch");
  14. }
  15.  
  16. if (!rect) {
  17. full_rect.x = 0;
  18. full_rect.y = 0;
  19. full_rect.w = texture->w;
  20. full_rect.h = texture->h;
  21. rect = &full_rect;
  22. }
  23.  
  24. if (texture->yuv) {
  25. return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);
  26. } else if (texture->native) {
  27. return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
  28. } else {
  29. renderer = texture->renderer;
  30. return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
  31. }
  32. }

从源码中能够看出,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. #ifdef USE_DYNAMIC_TEXTURE
  9. if (texture->access == SDL_TEXTUREACCESS_STREAMING &&
  10. rect->x == 0 && rect->y == 0 &&
  11. rect->w == texture->w && rect->h == texture->h) {
  12. full_texture = SDL_TRUE;
  13. }
  14. #endif
  15.  
  16. if (!data) {
  17. SDL_SetError("Texture is not currently available");
  18. return -1;
  19. }
  20.  
  21. if (D3D_UpdateTextureInternal(data->texture, texture->format, full_texture, rect->x, rect->y, rect->w, rect->h, pixels, pitch) < 0) {
  22. return -1;
  23. }
  24.  
  25. if (data->yuv) {
  26. /* Skip to the correct offset into the next texture */
  27. pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
  28.  
  29. 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) {
  30. return -1;
  31. }
  32.  
  33. /* Skip to the correct offset into the next texture */
  34. pixels = (const void*)((const Uint8*)pixels + (rect->h * pitch)/4);
  35. if (D3D_UpdateTextureInternal(texture->format == SDL_PIXELFORMAT_YV12 ?
  36.  
  37. data->utexture : data->vtexture, texture->format, full_texture, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, pixels, pitch / 2) < 0) {
  38. return -1;
  39. }
  40. }
  41. return 0;
  42. }

从代码中能够看出,该函数调用了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. if (full_texture) {
  11. result = IDirect3DTexture9_LockRect(texture, 0, &locked, NULL, D3DLOCK_DISCARD);
  12. } else {
  13. d3drect.left = x;
  14. d3drect.right = x + w;
  15. d3drect.top = y;
  16. d3drect.bottom = y + h;
  17. result = IDirect3DTexture9_LockRect(texture, 0, &locked, &d3drect, 0);
  18. }
  19.  
  20. if (FAILED(result)) {
  21. return D3D_SetError("LockRect()", result);
  22. }
  23.  
  24. src = (const Uint8 *)pixels;
  25. dst = locked.pBits;
  26. length = w * SDL_BYTESPERPIXEL(format);
  27. if (length == pitch && length == locked.Pitch) {
  28. SDL_memcpy(dst, src, length*h);
  29. } else {
  30. if (length > pitch) {
  31. length = pitch;
  32. }
  33. if (length > locked.Pitch) {
  34. length = locked.Pitch;
  35. }
  36. for (row = 0; row < h; ++row) {
  37. SDL_memcpy(dst, src, length);
  38. src += pitch;
  39. dst += locked.Pitch;
  40. }
  41. }
  42. IDirect3DTexture9_UnlockRect(texture, 0);
  43.  
  44. return 0;
  45. }

从代码中能够看出,该函数首先调用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. GL_ActivateRenderer(renderer);
  8.  
  9. renderdata->glEnable(data->type);
  10. renderdata->glBindTexture(data->type, data->texture);
  11. renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  12. renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,
  13. (pitch / SDL_BYTESPERPIXEL(texture->format)));
  14. renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
  15. rect->h, data->format, data->formattype,
  16. pixels);
  17. if (data->yuv) {
  18. renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / 2));
  19.  
  20. /* Skip to the correct offset into the next texture */
  21. pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
  22. if (texture->format == SDL_PIXELFORMAT_YV12) {
  23. renderdata->glBindTexture(data->type, data->vtexture);
  24. } else {
  25. renderdata->glBindTexture(data->type, data->utexture);
  26. }
  27. renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
  28. rect->w/2, rect->h/2,
  29. data->format, data->formattype, pixels);
  30.  
  31. /* Skip to the correct offset into the next texture */
  32. pixels = (const void*)((const Uint8*)pixels + (rect->h * pitch)/4);
  33. if (texture->format == SDL_PIXELFORMAT_YV12) {
  34. renderdata->glBindTexture(data->type, data->utexture);
  35. } else {
  36. renderdata->glBindTexture(data->type, data->vtexture);
  37. }
  38. renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
  39. rect->w/2, rect->h/2,
  40. data->format, data->formattype, pixels);
  41. }
  42. renderdata->glDisable(data->type);
  43.  
  44. return GL_CheckError("glTexSubImage2D()", renderer);
  45. }

从代码中能够看出。该函数调用了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. if(SDL_MUSTLOCK(surface))
  10. SDL_LockSurface(surface);
  11. src = (Uint8 *) pixels;
  12. dst = (Uint8 *) surface->pixels +
  13. rect->y * surface->pitch +
  14. rect->x * surface->format->BytesPerPixel;
  15. length = rect->w * surface->format->BytesPerPixel;
  16. for (row = 0; row < rect->h; ++row) {
  17. SDL_memcpy(dst, src, length);
  18. src += pitch;
  19. dst += surface->pitch;
  20. }
  21. if(SDL_MUSTLOCK(surface))
  22. SDL_UnlockSurface(surface);
  23. return 0;
  24. }

该函数的源码还没有具体分析。当中最关键的函数要数SDL_memcpy()了,正是这个函数更新了纹理的像素数据。可是Software渲染器纹理改动的时候是否须要Lock()和Unlock()呢?这一点一直也没太搞清。

SDL2源码分析5:更新纹理(SDL_UpdateTexture())的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. 一步步实现windows版ijkplayer系列文章之二——Ijkplayer播放器源码分析之音视频输出——视频篇

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

  7. jQuery1.6源码分析系列

    原文地址:http://www.cnblogs.com/nuysoft/archive/2011/11/14/2248023.html jQuery源码分析(版本1.6.1) 目录 00 前言开光 0 ...

  8. 一步步实现windows版ijkplayer系列文章之三——Ijkplayer播放器源码分析之音视频输出——音频篇

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

  9. jQuery1.6.1源码分析系列(作者:nuysoft/高云)

    作者:nuysoft/高云 QQ:47214707 Email:nuysoft@gmail.com jQuery源码分析(版本1.6.1) 00 前言开光 01 总体架构 02 正则表达式-RegEx ...

随机推荐

  1. 绑定运行计划sql_plan_baseline

    --因为生产环境运行的sql变化较快,版本号公布比較频繁,造成sql的运行计划不是非常稳定.常常会有一些性能非常查的sql出现 --对于这些sql,我们能够使用sql_plan_baseline对运行 ...

  2. Webserver管理系列:11、注意默认的隐含共享

    安装完Windows Server 2008之后默认的c/d/e...磁盘是共享的. 我们能够通过取消"Microsoft网络的文件和打印机共享"服务来阻止别人訪问我们的共享文件:

  3. IOS之【地图MapKit】

    iOS地图位置开发   iPhone SDK提供了三个类来管理位置信息:CLLocation CLLocationManager 和 CLLHeading(不常用).除了使用GPS来获取当前的位置信息 ...

  4. meminfo,df,

    yx100:root@yxRouter:/# cat /proc/meminfoMemTotal:         126584 kBMemFree:          103156 kBBuffer ...

  5. 【Demo 0008】标签控制器

    本章学习要点:       1.  了解标签控制器基础知识;       2.  掌握标签控制器层次结构;       3.  掌握标签控制器基本用法;       4.  掌握自定义标签控制器:   ...

  6. MySQL中同一时候存在创建和上次更新时间戳字段解决方法浅析

    在写这篇文章之前.明白我的MySQL版本号. mysql> SELECT VERSION(); +------------+ | VERSION() | +------------+ | 5.5 ...

  7. 与众不同 windows phone (19) - Device(设备)之陀螺仪传感器, Motion API

    原文:与众不同 windows phone (19) - Device(设备)之陀螺仪传感器, Motion API [索引页][源码下载] 与众不同 windows phone (19) - Dev ...

  8. 在bmp上添加字符

    //打开位图文件,得到位图句柄   HBITMAP OpenBmpFile(HDC hDC, LPSTR lpszFileName)   {       HBITMAP hBmp = NULL;    ...

  9. [Android学习笔记]View的measure过程学习

    View从创建到显示到屏幕需要经历几个过程: measure -> layout -> draw measure过程:计算view所占屏幕大小layout过程:设置view在屏幕的位置dr ...

  10. java垃圾回收那点事(二)不同gc策略的heap分配

    在前面的文章中曾提到了在java虚拟机启动的时候会对G1,CMS, SerialGC定义不同的heap的类,并且定义不同的policy. CollectorPolicy CollectorPolicy ...