0. Broadcom Nexus介绍:
Nexus是Broadcom提供的一套开发中间件,以标准API形式为Digital TV 和Set-Top boxes上层程序提供标准接口,它将不同的底层与上层开发隔绝开来,以达到上层开发与底层无关的目的。所有Interface的用法和思路基本上是一致的。Sam最先接触到的Nexus模块就是 Graphics.
1. Nexus Platform 接口和初始化:
Nexus有个API初始化各个模块以及硬件(NEXUS_Platform_Init())。应用程序也可以利用其参数定制初始化哪些模块以及动作。通常使用NEXUS_Platform_GetDefaultSettings()得到缺省的setting.修改想要修改的内容,之后再调用NEXUS_Platform_Init()初始化Platform.
NEXUS_PlatformSettings platformSettings;
NEXUS_Platform_GetDefaultSettings(&platformSettings);
//.... 修改必要的设置
NEXUS_Platform_Init(&platformSettings);
NEXUS_PlatformSettings结构体中可选择的选项通常有:I2C channel是否打开,是否FPGA channel打开,是否打开output等。
NEXUS_Platform_Init()会自动地打开和配置Interface.(如各种output等)。应用程序不应该再次去打开它,而是使用NEXUS_Platform_GetConfiguration()重新得到Handle.
NEXUS_PlatformConfiguration platformConfiguration;
NEXUS_Platform_GetConfiguration(&platformConfiguration);
NEXUS_PlatformConfiguration结构体包含如下内容:
NEXUS_I2cHandle i2c[NEXUS_NUM_I2C_CHANNELS]; //I2C channel.
我们通常最关心的是output Handle,
struct {
NEXUS_ComponentOutputHandle component[NEXUS_NUM_COMPONENT_OUTPUTS];
NEXUS_CompositeOutputHandle composite[NEXUS_NUM_COMPOSITE_OUTPUTS];
NEXUS_RfmHandle rfm[NEXUS_NUM_RFM_OUTPUTS];
NEXUS_SvideoOutputHandle svideo[NEXUS_NUM_SVIDEO_OUTPUTS];
NEXUS_AudioDacHandle audioDacs[NEXUS_NUM_AUDIO_DACS];
NEXUS_SpdifOutputHandle spdif[NEXUS_NUM_SPDIF_OUTPUTS];
NEXUS_I2sOutputHandle i2s[NEXUS_NUM_I2S_OUTPUTS];
NEXUS_HdmiOutputHandle hdmi[NEXUS_NUM_HDMI_OUTPUTS];
NEXUS_Ccir656OutputHandle ccir656[NEXUS_NUM_656_OUTPUTS];
} outputs;
分量,混合和Svideo output handle就在其中。
2. Graphics 具体讲解:
Nexus graphics 包含几个Interface. 各个Interface各自提供一些能力,应用程序可以将它们组合起来使用。
Interface包含如下:
Display: 是Graphics模块的核心,它指定哪个surface为framebuffer(当前显示的)。指定哪几个output会输出(composite,svideo等),以及如何将framebuffer合成到video上。
Surface: 其实就是一块内存。创建Surface时用参数指定:width, height, pitch, pixel format。长度x宽度x每个象素的长度就决定了Surface大小。可创建surface的个数限制与分配给Nexus的堆大小。
Graphics2D: 提供从一块surface blit(整块copy)到另一块surface的能力。提供填充一块surface 使用某种颜色和Alpha的能力。
Graphics3D: 提供适配一个3D graphics到Surface的能力。
VideoImageInput:Route graphics through the video feeder hardware (MFD)
2.1. Surface的创建(NEXUS_Surface_Create):
Surface创建使用NEXUS_Surface_Create创建。Surface的长度,宽度,深度,pixel format则可以由参数决定。
具体过程如下:
NEXUS_SurfaceHandle surface;
NEXUS_SurfaceCreateSettings createSettings;
NEXUS_Surface_GetDefaultCreateSettings(&createSettings);
createSettings.pixelFormat = NEXUS_PixelFormat_eA8_R8_G8_B8;
createSettings.width = 720;
createSettings.height = 480;
surface = NEXUS_Surface_Create(&createSettings);
NEXUS_SurfaceCreateSettings:结构体中包含了Surface的一些关键点:象素格式,调色板格式,surface长度,宽度。倾斜度(缺省为:象素长度x宽度,也就是每行的字节数)
2.2:Display的建立和配置:
2.2.1: Display的创建:
与Surface创建相同,Display创建时,也需要参数来指定Display的创建模式。
NEXUS_DisplaySettings displaySettings;
NEXUS_DisplayHandle display;
NEXUS_Display_GetDefaultSettings(&displaySettings);
displaySettings.format = NEXUS_VideoFormat_eNtsc;
display = NEXUS_Display_Open(0, &displaySettings);
NEXUS_DisplaySettings 结构体中包含Display的一些选项。其中format表明此Display将要输出的制式。它决定了TV中显示的宽,高。
2.2.2:Display显示输出channel的配置:
如前所述,在Platform初始化时,Output Interface已经被打开。此时需要得到它们的Handle. 给Display添加Output .
NEXUS_PlatformConfiguration platformConfig;
NEXUS_Platform_GetConfiguration(&platformConfig);
NEXUS_Display_AddOutput(display, NEXUS_ComponentOutput_GetConnector(platformConfig.outputs.component[0]));
NEXUS_Display_AddOutput(display, NEXUS_CompositeOutput_GetConnector(platformConfig.outputs.composite[0]));
此处Display添加了分量和混合2种输出。
2.2.3:Display Frame buffer的指定:
只有将某个Surface指定为Frame buffer, Surface中的内容才会按格式和设置显示到TV上。所以应用程序可以创建2-3个surface,轮流指定为Frame Buffer来加快速度。
NEXUS_Display_SetGraphicsFramebuffer(display, surface);
3. Graphics2D的建立和使用:
3.1:Graphics2D的建立:
Graphics2D的建立与其它Interface相同,需要参数指定Graphics2D的特征。
NEXUS_Graphics2DHandle gfx;
NEXUS_Graphics2DOpenSettings openSettings;
openSettings.preAllocPacketMemory = true; //if maxOperations is non-zero, this must be true.
openSettings.maxOperations = 100; //同步,异步blit的最大次数。如果是异步blit,则需要在blit次数超此次数之前使用NEXUS_Graphics2D_Checkpoint()。
gfx = NEXUS_Graphics2D_Open(0, &openSettings);
3.3: Graphics2D Setting:
NEXUS_Graphics2DSettings gfxSettings;
NEXUS_Graphics2D_GetSettings(gfx, &gfxSettings);
gfxSettings.checkpointCallback.callback = complete;
gfxSettings.checkpointCallback.context = event;
NEXUS_Graphics2D_SetSettings(gfx, &gfxSettings);
NEXUS_Graphics2DSettings比较重要的参数有:
bool blockedSync; //如果为true,则所有fill,blit会阻塞直到动作完成。(只有简单的程序会这么用)
此时,不需要NEXUS_Graphics2D_Checkpoint。
如果为false,则fill,blit不会被阻塞。所以需要NEXUS_Graphics2D_Checkpoint()来检测fill或blit动作是否已经完成。
NEXUS_CallbackDesc checkpointCallback; 在异步时使用,NEXUS_Graphics2D_Checkpoint()会调用它的callback。第一个参数为context.
callback function通常的处理是:BKNI_SetEvent(),发送一个event.
3.4:Graphics2D Fill:
在使用Fill功能时,需要先指定Surface. fill的颜色,Fill Surface的区域,color和alpha方式等。
NEXUS_Graphics2D_GetDefaultFillSettings(&fillSettings);
fillSettings.surface = framebuffer; //指定surface.
fillSettings.rect.width = createSettings.width; //如果要fill整个surface. x,y=0. width,height与surface宽高相同。
fillSettings.rect.height = createSettings.height;
fillSettings.color = 0; //色彩方式
NEXUS_Graphics2D_Fill(gfx, &fillSettings);
如果为异步方式(3.3中讲过),Fill后,必须加上NEXUS_Graphics2D_Checkpoint(gfx, NULL); 以确认Fill动作已真的完成。
注意:此处NEXUS_Graphics2D_Checkpoint(gfx, NULL); 只是察看Graphics Hardware是否正在Blit或Fill. 如果返回值为Success.表明当前硬件没有Blit或Fill. 如果返NEXUS_GRAPHICS2D_QUEUED,泽表明此时有Fill或Blit在排队。此时,则当Blit或Fill完成时,NEXUS_Graphics2DSettings::checkpointCallback 会被调用。
如3.3所讲,NEXUS_Graphics2D_Checkpoint()如果返回Success,则表明Fill完成,如果返回NEXUS_GRAPHICS2D_QUEUED,则在Fill动作完成后,自动调用callback function. Callback function又发送了一个event. 所以可以在NEXUS_Graphics2D_Checkpoint(gfx, NULL); 后添加:
BKNI_WaitForEvent();来确认Fill动作的完成。
3.5: Graphics2D Blit:
与其它动作类似,Blit也需要得到setting. 并设置想要blit的源sruface和目标surface. 以及各自的区域。
NEXUS_Graphics2DBlitSettings blitSettings;
NEXUS_Graphics2D_GetDefaultBlitSettings(&blitSettings);
blitSettings.source.surface = surface;
blitSettings.source.rect.x = 0;
blitSettings.source.rect.y = 0;
blitSettings.source.rect.width = 100;
blitSettings.source.rect.height = 140;
blitSettings.output.surface = surface;
blitSettings.output.rect.x = (rand() % (createSettings.width-120)) + 100;
blitSettings.output.rect.y = (rand() % (createSettings.height-20));
blitSettings.output.rect.width = 20;
blitSettings.output.rect.height = 20;
blitSettings.colorOp = NEXUS_BlitColorOp_eCopySource;
blitSettings.alphaOp = NEXUS_BlitAlphaOp_eCopySource;
rc = NEXUS_Graphics2D_Blit(gfx, &blitSettings);
4. Surface的长度,宽度的决定:
看到这个条目,可能会觉得有些奇怪,Surface的长宽,不是由自己指定的吗。呵呵,其实Sam的意思是决定Surface长宽的因素总结。
Surface如果想全屏显示于Display当前的video format下。则需要与video format长宽相同。
NEXUS_VideoFormat_GetInfo(displaySettings.format, &videoFormatInfo); //从display video 取出信息
createSettings.pixelFormat = NEXUS_PixelFormat_eA8_R8_G8_B8;
createSettings.width = videoFormatInfo.width; //讲长度宽度设置为与video format相同
createSettings.height = videoFormatInfo.height;
surface = NEXUS_Surface_Create(&createSettings);
还有一种情况比较特殊,下一节统一讲。
5.Display 如何放置Graphics Frame buffer于Video Window相关知识:
Display可以指定某个Surface为Graphics Frame buffer.但是Surface是否要全部显示,以及显示在TV屏幕的哪个位置,这是可设定的。下面就详细分析这个议题。
首先,有4个宽高值需要注意:
1. VideoWindow的宽高。
2. GraphicsFramebuffer的宽高。
3. Surface的宽高。
4. Display video format的宽高。
Display的video format决定了TV屏幕的大小,也就是VideoWindow的大小。所以Sam认为Dispaly Video format的宽高等同于VideoWindow的宽高。
GraphicsFramebuffer的大小应该是个虚值,可能与它指定的Surface相同。
Surface的大小可以自己定义,但哪一部份显示则由Display指定。
结构体NEXUS_GraphicsSettings中的值决定了Display如何将Graphics Frame buffer匹配于VideoWindow.也就是说将Graphics Frame Buffer如何放置于TV 屏幕。放置在什么位置,显示哪一部份Frame buffer内容等等。(所以前面说surface指定为framebuffer并不意味着它能全部显示)
NEXUS_Rect position;
NEXUS_Rect clip;
NEXUS_Rect结构体是个矩形,x,y表示左上角位置:
typedef struct NEXUS_Rect
{
int16_t x;
int16_t y;
uint16_t width;
uint16_t height;
} NEXUS_Rect;
position: 顾名思义,是说Display中Surface显示的部分。换句话说,Video Window(TV屏幕)的这一部分用来显示Framebuffer(Surface).
clip: Surface 中想要显示的部分。
当Display想要将surface显示于一个Video Window时,其实是将Surface中clip部分显示于Video window的Position部分。
我们会很快想到,如果position与clip大小不同怎么办?
当clip的宽度小于position的宽度时(高度相同),会将其水平拉伸。
当clip的高度小于position时(宽度相同),则无法显示。
当clip的宽度大于position的宽度时,则只显示一部分。
当clip的高度大于position的高度时,则只显示position部分。