Chinaunix首页 | 论坛 | 博客
  • 博客访问: 40791
  • 博文数量: 18
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 200
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-05 10:27
文章分类
文章存档

2011年(1)

2009年(1)

2008年(16)

我的朋友

分类:

2008-11-14 11:59:40

    一台数码相框,最基本的当然就是 show 图片了,包括 UI、用户图片等等。所以有必要详细了解整个过程,所以这是写此文章的目的。本文结合 sourcecode,力求整理出一条较为清晰的思路,如果错误或不够详细的地方,请在后面追加留言,我会进行重新整理。
 
从无到有:
    mmpM2dCreateNullSurface(smtkGuiMgrGetDisplayWidth(),
                            smtkGuiMgrGetDisplayHeight(),
                            MMP_PIXEL_FORMAT_RGB565, &stateSurf);
几乎每个 state 的Initialize,都会调用上面那个函数,此函数用于通过长宽得到了内存块大下,再赋值到  surface  结构体,然后将此结构体指针返回给 stateSurf,我们切进去看看:
前面两个参数比较简单,不详述。第三个参数,贴一段小知识:

色彩深度 color depth

 在图像中,它是由很多个点来组成的,那么存储每个像素点所用的位数就叫做像素深度。对一个图片,这个值是可以有所不同的,从而会使得图片的数据有多和少的区别。   一幅彩色图像的每个像素用 R,G,B 三个分量表示,若每个分量用8位,那么一个像素共用3X8=24位表示,就说像素的深度为24 bit,每个像素可以是2的24次方=16 777 216种颜色中的一种。表示一个像素的位数越多,它能表达的颜色数目就越多。   在用二进制数表示彩色图像的像素时,除 R,G,B 分量用固定位数表示外,往往还增加1位或几位作为属性(Attribute)位。例如,RGB 5∶5∶5表示一个像素时,用2个字节共16位表示,其中 R,G,B 各占5位,剩下一位作为属性位。在这种情况下,像素深度为16位,而图像深度为15 位。

 在用32位表示一个像素时,若 R,G,B 分别用8位表示,剩下的8位常称为 alpha 通道(alpha channel)位,或称为覆盖(overlay)位、中断位、属性位。它的用法可用一个预乘 α 通道(premultiplied alpha)的例子说明。假如一个像素(A,R,G,B)的四个分量都用归一化的数值表示,(A,R,G,B)为(1,1,0,0)时显示红色。当像素为 (0.5,1,0,0)时,预乘的结果就变成(0.5,0.5,0,0),这表示原来该像素显示的红色的强度为1,而现在显示的红色的强度降了一半。   这个 alpha 值,在这里就用来表示该像素如何产生特技效果。   总体来说,图像的宽高、分辨率越高,就是组成一幅图的像素越多,则图像文件越大;像素深度越深,就是表达单个像素的颜色和亮度的位数越多,图像文件就越大。

第四个参数stateSurf,是故事的主角,一层一层跟进去,找到了他的原型

“typedef void* MMP_SURFACE;”

所以他是一个空指针,这种用法在我们的程序里面用得很多,应该是灵活的原因吧!

进入程序

//宽的方向,字节对齐
    MMP_UINT        imageDelta = M2D_PITCH_ALIGN(M2D_BPP[imageFormat] * imageWidth);
//高或宽度为 0 ,返回错误信息退出
    if ((imageWidth == 0)
        || (imageHeight == 0))
    {
        return MMP_RESULT_ERROR;
    }
//已对齐的宽,乘以图像高度,并将结果以 byets 的方式再次对齐
    displaySize = M2D_02_BYTES_ALIGN(imageDelta * imageHeight);
//内存空间申请,并将内存首地址指针赋给 baseScanPtr
    baseScanPtr = (MMP_UINT8*)MEM_Allocate(displaySize, MEM_USER_M2D, MEM_USAGE_GENERAL);
//内存空间申请失败
    if (baseScanPtr == NULL)
    {
        return MMP_RESULT_ERROR;
    }
// Create a empty display context for the existing memory space.
//并对结构体进行空间分配及元素初始化,最后将此结构体的首地址返回
    newDisplay = M2D_CreateSurface(imageWidth,
                                   imageHeight,
                                   imageDelta,
                                   imageFormat,
                                   M2D_DATA_IN_VRAM,
                                   (const void *)baseScanPtr);

可以看到程序先对 imageWidth 做了一个数据对齐的处理,得到 imageDelta,接着将此值乘以高度,得到 displaySize,并且对此值再做一次对齐处理!得到了所需的 stream 的大小,然后根据 baseScanPtr 的大小,M2D_CreateSurface,再把这个 surface 的头指针赋给newDisplay ,然后后面再用 *display = (MMP_M2D_SURFACE)newDisplay;,将此首地址传出去给我们前面说的 &stateSurf 。  

PS:数据对齐是为了加快数据访问的一种技术,但是这里看起来好像并不是这个目的,是否跟图片的显示有关系呢?有待验证。

 

至此,我们已经在 SDRAM 里面开辟了一块区域,为图片的显示做好了准备。接着,就要把真实的图片data 放到里面去,这时候有两个方式:

#ifdef SMTK_RESOURCE_DYNAMIC_LOADING

    smtkFileMgrResourceIsExist(FileName);

    result = smtkGuiMgrCreateJpgToSurface((MMP_WCHAR*) FileName,

                                                                            Surface,

                                                                            FileWIDTH,

                                            FileHEIGHT,

                                                                            SMTK_GUI_FIT);

#else

         result = smtkJpegMgrSimpleDecode( (MMP_UINT8*)BitmapJpg,

                                                                                      sizeofBitmapJpg,

                                                                                      Surface,

                                                                                      BITMAP_WIDTH,

                                                                                      BITMAP_HEIGHT);     

#endif

 

第一种方法:smtkGuiMgrCreateJpgToSurface

                此方法依赖于 filesystem,它可以直接傳進filename, 不必自己處理load file的動作是比较方便的。使用这种方法,在文件头的地方都有类似:

       #ifdef SMTK_RESOURCE_DYNAMIC_LOADING

static const PAL_TCHAR* FileName = RESOURCE_TDEFAULT PAL_T("FileName.jpg");

       这样的定义。其中

#define RESOURCE_TDEFAULT            NOR_VOLUME L"smediadata/"

       这样就找到了这个图片在 filesystem中的路径了。

         切进去函数 smtkGuiMgrCreateJpgToSurface,可以看到程序先调用 smtkJpegMgrStartDecode jpeg 做解码,然后紧解着调用 smtkJpegMgrGetDecodeStatus 查询解码的情况,如果已经解完,则退出;否则一直在那边 polling decodeStatus。这个地方有两个问题需要注意:

1、                 进入 smtkJpegMgrStartDecode,可以看到里面有 PalSetEvent(smtkJpegMgr.eventMgrToThread); 这样一行。这儿函数,产生了一个线程事件后,就退出了,继续跑后面的程序。这样来看,jepg 的解码,是一个独立的进程,所以,smtkJpegMgrStartDecode 执行完成,并不意味着解码结束!这也是后面用一个 while 可以polling decodeStatus 的原因。

2、                 在用while polling decodeStatus 的时候,里面除了用smtkJpegMgrGetDecodeStatus 来得到解码的状态,还调用了 PalSleep(10); 这一句非常重要!因为我们的系统是一个多线程的系统,所以这样做后,可以使得这个 while,不会始终占用着 CPU 的资源,而使其他线程可以跑起来。例如在 slidershow +music 的时候,这样做,可以让后面的music 继续播,而不会因为 CPU 资源被占而停掉。至于要 sleep 多久,可以只能慢慢调节,取一个好一些的效果,不过一般用 10 比较多。

回头再看 smtkJpegMgrStartDecode,它其实只是将当前准备解码的图片的信息,传给了 smtkJpegShareData 这个结构体,然后就调用 PalSetEvent jpeg decode 线程跑起来。再往后,就是很复杂的操作系统的东西了,信号量、消息队列等等都跑了出去。这些比较复杂,让操作系统去处理就好了。

第二种方法:smtkJpegMgrSimpleDecode通常是用來decode file型態的jpeg,如果 nor 的空间够,我们也可以都用这个方式,去将图片串成一个 array,然后直接对其操作。

阅读(652) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~