加载bpg图像:兼容,简单,高效的技术

时间:2014-12-21 02:07:37

标签: javascript image image-processing bpg

最近有一些关于Fabrice Bellard的BPG图像格式(http://bellard.org/bpg/)的喧哗,基于他网站上提供的演示,它提供了比jpeg,webp和其他一些更好的压缩。图像解码在浏览器中使用JS完成,这意味着它可以立即使用,无需等待浏览器采用。总的来说,这似乎是一个好主意,交换一些CPU时间来加快下载速度通常是可行的权衡。

交换图像的used here技术是在window.load上迭代document.images,找到src属性包含以" .bpg"结尾的URL的任何位置。并用画布替换它。

然而,这绝对不是解决问题的唯一方法,我看到这种技术的一些缺点,包括:a)画布与图像没有完全相同的布局规则 - 例如在它上面设置宽度属性意味着在img和画布上有不同的东西,b)至少在Chrome中,对于缩小与画布相比的图像,缩放的方式也是不同的。

理想情况下,更好的解决方案可以满足这些要求:

  • 尝试不再需要在内存中复制图像数据(并且不会不必要地使用超过必要的CPU) - JS中的解码与本机图像处理相比需要很多)
  • 尽可能多的浏览器兼容性
  • 使用< img>标签而不是< canvas> (不是要求,但似乎更好)
  • 提供一种简单的方法,不仅可以处理文档加载上的图像,还可以处理稍后添加到文档中的图像(例如,响应用户活动)
  • 仍然易于使用(bellard.org上的现有技术肯定易于集成)
  • 编辑:使用网络工作者解码图像而不阻止页面也是一个很好的方法。

我们想到的一些相关工具包括数据:和blob:urls。

任何人都有工作代码的例子,这些代码使用这样的"更好的"来加载BPG。技术? (Fabrice在他的例子中使用它的方式并不糟糕,当然方法有权衡,但我认为可能有更好的广泛使用。)

2 个答案:

答案 0 :(得分:2)

BPG确实看起来很有希望。如果您想在任何时间从任何来源检测到<img>元素的添加,您可以使用MutationObservers。 如果您不了解它们,它们是异步的,记录文档中所有DOM突变的子集,并允许回调一次处理这些突变,而不是像DOM事件那样同步。因此,如果您在脚本中创建或更改大量图像的来源,观察者将等待您的脚本完成,然后一次性处理所有新图像(因此回调中的循环)。

以下假设你有一个名为doSomethingToDecode(img)的函数(对不起,我现在不会帮忙)用img替换src生成的PNG(最有可能) 。它可以异步执行,这不是问题。此外,只要生成的重新加入不以“.bpg”结尾,您就不需要在交换src时停止观察图像。

当你在任何地方添加<img>个后代时,第一个观察者会做出反应(和其他元素一样,但忽略了这些元素);遗憾的是,在一般情况下不可能对其进行大量优化。它必须迭代突变列表,然后迭代每个突变的新节点列表,因此嵌套for循环。但是

imgObs=new MutationObserver(
    function(mutations){
        for(var i=0, m; m=mutations[i]; i++) if(m.addedNodes.length)
            for(var j=0, img; img=m.addedNodes[j]; j++) if(img.localName=="img"){
                if(img.src && /\.bpg$/.test(img.src))
                    doSomethingToDecode(img)
                srcObs.observe(img, srcOptions)
            }
    }
)

当你更改元素的src属性时,另一个观察者会做出反应,并且应该只观察<img>个元素(为了获得最佳性能,如果你观察其他元素,它没有故障,所以别)。

srcObs=new MutationObserver(
    function(mutations){
        for(var i=0, m; m=mutations[i]; i++){
            var img=m.target
            if(img.src && /\.bpg$/.test(img.src))
                doSomethingToDecode(img)
        }
    }
)

同时保持这个方便,我们每次开始观察新图像时都需要它:

var srcOptions={childList:false, attributes:true, attributeFilter:["src"]}

您还可以让观察者对<img>元素的删除作出反应,然后停止观察它们,并释放任何与解码相关的资源,但希望浏览器至少足够智能,以便停止观察应该垃圾收集(未经测试!)。

加载HTML后运行此命令(不要等待整个页面包含图像和CSS等)。注意:这是使用DOM级别0 document.images集合。非常老派,但它正在非常有效和简洁地完成我们想要的,所以为什么不呢?

首先,您使用bpg源解码现有的<img>,然后观察它们src更改:

for(var i=0, img; img=document.images[i]; i++){
    if(img.src && /\.bpg$/.test(img.src))
        doSomethingToDecode(img)
    srcObs.observe(img, srcOptions)
}

然后这告诉第一个观察者对整个文档<body>中的内容作出反应;遗憾的是,没有tagNameFilter参数来原生过滤掉非<img> childList突变。

imgObs.observe(document.body, {subtree:true, childList:true, attributes:false})

答案 1 :(得分:0)

为图像添加错误事件处理程序。在Chrome 40上,当图像丢失或浏览器不知道如何显示时会触发它:

<script>
var errors = [];
function fallback(elt) {
    if(errors[elt.src]) return;
    // you should also extract the extension in order to test lena.bpg, lena.png, lena.jpg and not lena.bpg.png, lena.bpg.png.jpg
    errors[elt.src] = true;
    console.error('could not load image, falling back');
    elt.src = elt.src + '.png';
    // stop trying
    elt.onerror= function(){};
}
</script>
<img src="test.html" onerror="fallback(this);" width=300 height=300 />

优点:客户端部分没有内存/ CPU使用情况。 缺点:需要在服务器上提供备用图像。

我相信你可以在所有img标签上声明一个全局错误监听器,这样也可以用于未来的图像。