cqcqwind 发表于 2022-5-8 10:05

【平头哥RVB2601创意应用开发】动态加载MBRE JPEG解码器移植源码与测试结果

<p>&nbsp;&nbsp;&nbsp;&nbsp;JPEG是数字图像的有损压缩的常用方法,特别是对于由数码摄影产生的图像。JPEG(Joint Photographic Experts Group)是在国际标准化组织(ISO)领导之下制定静态图像压缩标准的委员会,第一套国际静态图像压缩标准ISO 10918-1(JPEG)就是该委员会制定的。由于JPEG优良的品质,使他在短短几年内获得了成功,被广泛应用于互联网和数码相机领域,网站上80%的图像都采用了JPEG压缩标准。相比无损压缩的PNG, GIF等格式, JPEG提供了可控质量损失下高压缩比的存储能力。</p>

<p>&nbsp; &nbsp;TJpgDec是一个专门为小型嵌入式系统高度优化PEG图像的解码模块,具有几个非常明显的特点:</p>

<ul>
        <li>平台独立。使用ANSI-C编写,支持ansi-c的平台系统移植起来没有任何障碍;</li>
        <li>易于使用的操作模式,输出可配置YUV或者RGB888、565格式,回调方式完成输出;</li>
        <li>
        <p>完全可重入的体系结构,解码成功后无需任何重启操作,便可直接重入执行新的解码</p>
        </li>
        <li>
        <p>超小的内存占用,最小配置下,只需要3100字节的工作内存。因采用分块解码设计,不管多大的原始图像尺寸都不会消耗更多工作内存。ROM占用也只有不到10K</p>
        </li>
</ul>

<p>&nbsp; &nbsp;<span style="font-size:16px;">TJpgDec的作者<small>Bodmer就是TFT-espi的作者,所以两者的结合更是天衣无缝,使用起来非常简单。</small></span></p>

<p><span style="font-size:16px;"><small>&nbsp; &nbsp;本次MBRE测试动态加载的过程中, JPG作为小电视项目的支撑库之一, 也做了移植和测试。 因为RVB2601没有直接提供spiffs,所以只做了内存数组解码的原理测试(从文件系统中操作本质其实相同,只需要增加对文件系统的访问接口即可)。移植过程主要是修改一些包含的头文件已保证编译通过即可,解码器源代码请参考附件,</small></span><br />
<br />
<span style="font-size:16px;"><small>基本上没有遇到任何问题。</small></span></p>

<p><span style="font-size:16px;"><small>&nbsp; 解码器配合TFT-ESPI使用起来就非常简单了,因为解码器配置成了RGB 565的输出,和实验用的ILI9341的输出格式一致,因此只需在初始化时指定好输出回调函数,并在回调函数中将解码得到的位图贴到TFT上即可。这里给出一个小例子:</small></span></p>

<pre>
<code class="language-cpp">    //JPG Decoder的初始化设置
    TJpgDec.thisPtr = &amp;TJpgDec;       //移植版本去掉了构造函数,手工处理原构造函数中的指针赋值操作
    TJpgDec.setJpgScale(1);         //缩放比例,设置为1
    TJpgDec.setSwapBytes(false);      //565标准格式,无需交换RGB bytes
    TJpgDec.setCallback(tft_output);//解码输出回调,每解码一部分,产生小块位图,就会回调一次

    //以下解码RAM中的JPG数组,并计算解码,刷新时间显示在屏幕中
        time_t t0 = millis();
    TJpgDec.drawJpg(0, 0, testjpg_data, sizeof(testjpg_data));
        time_t t1 = millis();            

        char buf;
        sprintf(buf, "JPG解码测试 %dms", t1 - t0);
    //使用TFT-espi的API显示文字
    gpTft-&gt;drawString(buf, 8, 10, MBRE_FIXED_FONT);
</code></pre>

<p>&nbsp;</p>

<p>&nbsp; &nbsp;上述代码中的tft_output是回调函数,JPG解码器是一小块一小块的解码(这样可以控制RAM占用),解码一个块之后就会调用设置好的回调函数进行实际的绘制处理。给一个使用tft_espi的tft_output参考实现:</p>

<pre>
<code class="language-cpp">// TFT屏幕输出函数
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
{

    //简单的裁剪保护,超过屏幕Y最大值不用绘制了
    //并返回0,通知解码器不用再进行后续解码
    if(y &gt;= gpTft-&gt;height())   return 0;

    //将位图通过Tft-espi绘制到屏幕上, tft-espi会对位图做基本的裁剪保护
    gpTft-&gt;pushImage(x, y, w, h, bitmap);

   
    // 返回1告诉解码器继续解码
    return 1;
}</code></pre>

<p>最后上图,在RVB2601上解码320x240 QVGA分辨率 jpeg的实测结果。&nbsp; 图示的JPEG解码和刷屏时间大概190MS,考虑到单刷屏时间在60-100ms之间, 解码时间大概是90-130ms,是一个可以接受的指标。更多关于JPG DECODER的实际使用,建议大家参考MBRE前次分享中提及的小电视项目的开源代码学习。其中的太空人飞行动画就是采用JPG连续解码+TSprite离屏缓冲实现的平滑动画效果。</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">&nbsp;</p>

Jacktang 发表于 2022-5-9 08:06

<p>解码器配置成了RGB 565的输出,配合TFT-ESPI,初始化时指定好输出回调函数,比较简洁简单明了</p>
页: [1]
查看完整版本: 【平头哥RVB2601创意应用开发】动态加载MBRE JPEG解码器移植源码与测试结果