【问题标题】:Synchronous loading of Base64-encoded images in Javascript在 Javascript 中同步加载 Base64 编码的图像
【发布时间】:2014-08-05 01:08:46
【问题描述】:

我有一组 ~200 个 base64 编码的 PNG(全部来自一个共享的 JSON 文件),我想直接在循环中加载它们作为 WebGL 纹理,而不必求助于回调/事件处理程序(对于更简单的代码和更少的事件处理开销)。有没有办法在 Javascript 中做到这一点?

我知道加载 base64 编码图像的规范方法是通过数据 URI。但似乎设置Image 对象的src 属性实际上并不解码图像数据,而只是将其排队以进行异步加载(这意味着在设置@987654324 之后立即将Image 对象作为WebGL 纹理上传@属性失败;设置上传纹理的onload处理程序成功)。

【问题讨论】:

    标签: javascript image base64 webgl


    【解决方案1】:

    为什么?为什么要同步进行?浏览器中的 JavaScript 并非旨在同步运行。您将冻结浏览器并可能出现“此脚本执行时间过长”对话框。

    异步处理并不难,为什么不这样做呢?

    var loadTextureFromImage = function(url, callback) {
       // use cyan as the default color.
       var initialColor = new Uint8Array([0,255,255,255]);
    
       // make a texture with 1x1 pixels so we can use the texture immediately
       // while we wait for the image to load
       var tex = gl.createTexture();
       gl.bindTexture(gl.TEXTURE_2D, tex);
       gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, 
                     gl.RGBA, gl.UNSIGNED_BYTE, initialColor); 
    
       var img = new Image();
       img.onload = function() {
          gl.bindTexture(gl.TEXTURE_2D, tex);
          gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
          callback(img);
       };
    
       return tex;
    };
    
    var loadTexturesFromImages(listOfImages, callback) {
       var numLeftToDownload = listOfImages.length;
    
       var checkFinished = function() {
         --numLeftToDownload;
         if (numLeftToDownload == 0 && callback) {
           callback();
         }
       };
    
       return listOfImages.map(function(url) {
         return loadTexturesFromImage(url, checkFinished);
       });
    };
    

    这里有一些代码来使用上面的函数

    var imagesToLoad = [
       "http://foo.com/image1.jpg",
       "http://foo.com/image2.jpg",
       "data:image/png;base64,iVBORw0KG...",
    ];
    
    var textures = loadTexturesFromImages(imagesToLoad);
    

    现在您可以开始渲染了。它们都将以青色呈现,并在下载/解码时更新为图像。

    如果您想等到它们全部下载完毕,请向loadTexturesFromImages 提供回调。

    我想你的问题的简短答案是,“不,你不能同步解码图像”。长答案是如果你想同步解码它们you'll have to decode them yourself。但现在你又回到了上面提到的问题。也就是说,您将冻结浏览器,您的用户可能会收到一个对话框,询问他们是否要终止脚本。

    【讨论】:

    • 我知道异步方式并不难,在提问之前我已经实现并测试过了。它只是过于复杂并且可能很慢。它很慢,因为我要加载大约 200 个纹理,每个纹理都会产生调度解码、调度 onLoad 事件处理程序和执行它的开销。而且每个纹理的大小都小于 20kB,因此冻结浏览器不是问题。
    • 使用 png.js 或 emscripten-ed 版本的 libpng 自行解码是一个巧妙的技巧,但有点违背了获得性能的目的。
    • 你认为它为什么会获得性能?因为解码是异步的,所以每个图像都可以在不同的线程中解码,而同步则不是这样。而且,即使浏览器在同一个线程上对它们进行解码,异步响应 JavaScript 的开销也非常小,以至于几乎无法测量差异。所以真正唯一不同的同步与异步是异步有机会大大加快,否则它们只是不同的编码风格。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 2016-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多