第一次使用另一个画布作为源参数时,画布绘制图像速度较慢

2023-12-19

第一次使用另一个画布作为绘图源时,我看到画布绘制速度很慢。随后的 canvas 到 canvas .drawImage 调用都很好,直到我交换图像(然后我再次看到相同的问题)。

下面的示例代码 - 加载图像,然后创建 4 个画布,第一个画布从图像本身绘制,第二个画布从第一个画布绘制,依此类推。创建画布后,交换源图像并运行代码再次。

        var sourceImage = new Image();  // Original image
        var myImages = []; // Array of image and canvases references
        myImages[0] = sourceImage; // Set first myImage to image source

        // Image onload 
        sourceImage.onload = function () {
     
            console.log("Imageload", new Date() - t0);
            myImages[0] = sourceImage;

            // Loop to create and draw on canvases
            for (var i = 1; i <= 4; i += 1) {

                // Create canvas
                myImages[i] = document.createElement("canvas");

                // Set canvas dimensions to same as original image
                myImages[i].width = myImages[0].width;
                myImages[i].height = myImages[0].height;

                // Draw last canvas / image onto this canvas
                t0 = new Date();
                myImages[i].getContext("2d").drawImage(
                    myImages[i - 1],
                    0,
                    0,
                    myImages[i].width,
                    myImages[i].height
                );
                console.log("drawImage", i,  new Date() - t0); 
                
            }

            // Finished with black.jpg so load white.jpg           
            if (sourceImage.getAttribute("src") == "images/black.jpg") {
                sourceImage.src = "images/white.jpg"
            }

        }

        // Load image
        t0 = new Date();
        sourceImage.src = "images/black.jpg"

控制台输出是...

Imageload 36
drawImage 1 0
drawImage 2 255
drawImage 3 0
drawImage 4 0

Imageload 35
drawImage 1 0
drawImage 2 388
drawImage 3 1
drawImage 4 1

我的问题是为什么第二个画布绘制速度很慢?我尝试过各种图像文件和不同的画布尺寸,但总是看到相同的结果。我已经在 Chrome 和 Safari 上进行了测试。

如果缓慢绘制是在第一个画布上,我可以接受虽然 .onload 已触发,但图像仍然存在一些问题。但速度慢的是第二个画布,即第一个画布是从图像中毫无问题地绘制的。


我认为你在这里遇到了一个奇怪的优化怪癖,可能很难对发生的事情有一个明确的答案,但无论如何我会尝试做出一个有根据的猜测。


浏览器似乎在 GPU 实际执行分配给它的作业之前就回到了 CPU 线程,充分利用了任务并行性。

所以在第一个循环中,GPU 将开始一项任务,要求绘制<img> to the <canvas>,即使在 Chrome 中解码该图像数据,仍然必须将其传输到 GPU 并转换为实际的位图。
但正如我们所说,这是并行完成的,因此 js 脚本可以继续并直接进行第二个循环在做这份工作的同时.

然而,当在第二个目标画布上绘制第一个目标画布时,它会发现有一个正在运行的 GPU 作业将修改它,因此会阻塞 CPU 线程,直到第一个绘图完成。

下一次迭代虽然只会处理<canvas>位图缓冲区已经在 GPU 上的来源,因此它们不会花费任何相关时间。

我们可以通过在每次迭代之间等待几毫秒来以某种方式确认这一点。这样做,所有画布到画布操作大约需要0ms。

var url1 = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png?bar" + Math.random();
var url2 = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png?foo" + Math.random();

var sourceImage = new Image(); // Original image
sourceImage.crossOrigin = true;
var myImages = []; // Array of image and canvases references
myImages[0] = sourceImage; // Set first myImage to image source

// Image onload 
sourceImage.onload = async function() {

  console.log("Imageload", new Date() - t0);
  myImages[0] = sourceImage;
  // create canvases before hand to be sure it's not part of the issue
  for (var i = 1; i <= 4; i += 1) {
    // Create canvas
    myImages[i] = document.createElement("canvas");

    // Set canvas dimensions to same as original image
    myImages[i].width = myImages[0].width;
    myImages[i].height = myImages[0].height;
    myImages[i].getContext("2d");
  }

  // Loop to create and draw on canvases
  for (var i = 1; i <= 4; i += 1) {
    // Draw last canvas / image onto this canvas
    t0 = new Date();
    myImages[i].getContext("2d").drawImage(
      myImages[i - 1],
      0,
      0,
      myImages[i].width,
      myImages[i].height
    );
    console.log("drawImage", i, new Date() - t0);
    await new Promise(r => setTimeout(r, 500));

  }

  // Finished with black.jpg so load white.jpg           
  if (sourceImage.getAttribute("src") == url1) {
    sourceImage.src = url2
  }

};

// Load image
t0 = new Date();
sourceImage.src = url1;

类似地,如果我们确实生成每个源的 ImageBitmap,我们可以看到花费最多时间的那个是预期的<img>:

var url1 = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png?bar" + Math.random();
var url2 = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png?foo" + Math.random();

var sourceImage = new Image(); // Original image
sourceImage.crossOrigin = true;
var myImages = []; // Array of image and canvases references
myImages[0] = sourceImage; // Set first myImage to image source

// Image onload 
sourceImage.onload = async function() {

  console.log("Imageload", new Date() - t0);
  myImages[0] = sourceImage;
  // create canvases beforehand to be sure it's not part of the issue
  for (var i = 1; i <= 4; i += 1) {
    // Create canvas
    myImages[i] = document.createElement("canvas");

    // Set canvas dimensions to same as original image
    myImages[i].width = myImages[0].width;
    myImages[i].height = myImages[0].height;
    myImages[i].getContext("2d");
  }

  // Loop to create and draw on canvases
  for (var i = 1; i <= 4; i += 1) {
    // wait for create ImageBitmap to be created
    t0 = new Date();
    const img = await createImageBitmap(myImages[i - 1]);
    console.log("createImageBitmap", i, new Date() - t0);

    t0 = new Date();
    myImages[i].getContext("2d").drawImage(
      img,
      0,
      0,
      myImages[i].width,
      myImages[i].height
    );
    console.log("drawImage", i, new Date() - t0);

  }

  // Finished with black.jpg so load white.jpg           
  if (sourceImage.getAttribute("src") == url1) {
    sourceImage.src = url2
  }

};

// Load image
t0 = new Date();
sourceImage.src = url1;

PS:有人可能会忍不住打电话getImageData强制同步返回 CPU 线程,但这样做时,我们还会在 CPU 和 GPU 之间来回传输所有画布位图,从而在每个循环中实际上产生相同的速度。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

第一次使用另一个画布作为源参数时,画布绘制图像速度较慢 的相关文章

  • 茉莉花节点没有输出

    我是 JavaScript Node js 和 jasmine 的新手 我正在尝试运行 Node Craftsman Book 一书中的测试 FilesizeWatcher 我创建了 package json 文件并运行 npm insta
  • 邮件附件媒体类型错误 Gmail API

    我正在尝试通过 Javascript 客户端中的 Gmail API 发送带有附加 jpeg 文件的消息 到目前为止我写的代码如下 ajax type POST url https www googleapis com upload gma
  • 最小的 ExtJS 包是什么?

    有谁知道 Ext JS 2 2 所需的最少文件吗 我知道 ExtJS 网站有一个功能 build http extjs com products extjs build ExtJS ext js 的小版本 作为 ext all js 的替代
  • 如何在 Chrome 中实现抓取光标图标?

    我知道可以在 Chrome 中使用抓取光标图标 当然是在 Gmail 中 但我不知道如何在我的代码中实现它 我已经尝试过 在CSS中 body cursor grab body cursor webkit grab body cursor
  • Chrome 扩展程序中的后台脚本到底何时运行?

    在我的 chrome 扩展中 我有一个后台脚本 它将使用XMLHttpRequest note that this code is in the global scope i e outside of any function also n
  • 代码点火器 JSON

    你好 我使用 codeigniter 然后我从控制器中的数据库中回显输出 然后在我的视图文件中执行以下操作 但它没有显示任何内容 S 我的模型文件 function forumList this gt db gt select oversk
  • WebDriver:更改事件未触发

    我有一个使用 KnockoutJS 的应用程序 我正在尝试编写一些测试表单的测试 如果您不了解 KnockoutJS 简单来说 它提供了从我的视图到我的数据模型的绑定 这意味着当我在输入字段中键入值时 我的基础对象会自动使用该输入字段值进行
  • 使用 easyXDM 调整 IFrame 大小

    我将 iFrame 代码提供给客户 以便他们可以显示我网站上的动态内容 我希望其页面上的 iFrame 能够调整大小以适合我的内容 我按照 easyXDM 网站的说明进行操作 但也许我遗漏了一些东西 我没有收到任何错误 但 iFrame 保
  • 如何获取数组中最后 5 个元素(不包括第一个元素)?

    在 JavaScript 数组中 如何获取最后 5 个元素 排除第一个元素 1 55 77 88 would return 55 77 88 添加其他示例 1 55 77 88 99 22 33 44 would return 88 99
  • NodeJS 无法加载 css 文件

    所以我正在尝试制作一个 NodeJS 服务器 并且我尝试保留尽可能少的附加组件 但是 我遇到了一个问题 我似乎无法加载任何内容CSS我调用的文件HTML文件 该调用似乎确实由服务器处理 但它不会显示在浏览器中 My 网络服务器 js fil
  • t /= d 是什么意思? Python 和错误

    t current time b begInnIng value c change In value d duration def easeOutQuad swing function x t b c d alert jQuery easi
  • 正则表达式 - 避免表达式中出现字符串

    我正在尝试创建一个应该匹配以下情况的正则表达式 如果单词完全匹配 first second third 那么匹配应该失败 但如果它周围有任何字符 那么应该匹配该字符串 我还需要避免字符串中的某些字符集 如果这些字符是字符串的一部分 则匹配结
  • Rxjs 可观察等待直到满足某些条件

    我有以下重试逻辑来重试操作 对于单个请求来说它工作得很好 对于多个正在进行的请求 我想在重试之前等待现有的重试逻辑完成 handleError errors Observable
  • 如何在 OpenLayers 3 中删除监听器

    我做了一个copy https gis stackexchange com questions 178222 how to delete a listener in openlayers 3我在 stackoverflow 上提出的问题 因
  • 单击react.js 切换列表的背景颜色

    我正在尝试创建一个具有以下功能的列表 悬停时更改列表项的背景颜色 单击时更改列表项的背景颜色 在单击的元素之间切换背景颜色 即列表中只有一个元素可以具有 clicked 属性 我已经执行了 onhover 1 和 2 功能 但无法实现第三个
  • Javascript - 如何计算数字的平方?

    使用 JavaScript 函数 function squareIt number return number number 当给定数字 4294967296 时 函数返回 18446744073709552000 每个人都知道真正的答案是
  • 在 中动态添加链接样式表 [关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 如何将链接
  • nvd3.js - 无法更改折线图中线条的颜色

    我正在尝试更改 nvd3 折线图不同线条的颜色here http nvd3 org livecode index html codemirrorNav但我无法理解该怎么做 我想将示例中的 2 条线的颜色更改为绿色和青色 我试过 nv add
  • 盒式捆绑包与 MVC4 捆绑包

    我目前正在开发一个原型 ASP NET MVC 3 解决方案 该解决方案将用作多个项目重写的基础 来自 Web 表单 我的目标之一是跨应用程序实现一些脚本管理 而不是我们目前没有的目标 MVC 3有一个缺陷恕我直言 如果您需要在部分视图或模
  • Angular 5 中 Observable.ForkJoin 的重复 Http 请求

    我有一个 Angular 5 应用程序 组件中包含以下代码 ngOnInit Observable forkJoin this highlightedInsight this insightService getHighlightedIns

随机推荐

  • .animate() - 旧版 jquery 版本 (Drupal) 冲突的队列模拟

    我正在寻找一个解决方案来推出 jquery 版本 Drupal 本身就包含该版本 它是旧版本 实际上没有任何问题 但有一个 D 我使用队列为 false 的 animate 函数 并且没有此属性 因为此属性在 jquery 1 7 中添加到
  • NSJSONSerialization 拆箱 NSNumber?

    我在用着NSJSONSerialization转动一个JSON将文档转换为核心基础类型 我的领域里有一个JSON这是一个 数字 有时是整数 有时是浮点数 现在 问题是何时NSJSONSerialization变成我的JSON进入一个NSDi
  • Git:存储但不拉取较少的文件

    我最近开始使用 LESS 当我对 Jenkins 和 Grunt 等自动化构建器进行一些研究时 似乎一个常见的建议是不要将 LESS 文件存储在存储库上 或者不要将它们放在实时服务器上 只是编译的 CSS 所以我希望能得到一些关于这方面的建
  • 使用 Javascript 更改页面

    我构建了一个 Phonegap 应用程序 我有 7 8 个页面 我需要使用 Javascript 在它们之间导航 我尝试过使用window open and window location但那些不起作用 如何使用 Javascript 更改
  • Intellisense 将 .c 文件视为 .cpp

    我正在使用 VS2010 进行 C 项目 我不断收到 Intellisense 错误IntelliSense a value of type void cannot be assigned to an entity of type Blah
  • 在 Eclipse“运行方式 -> Android 应用程序”构建中包含 Maven 依赖项

    我使用eclipse开发一个Android应用程序 Android 开发工具 ADT 插件 http developer android com guide developing eclipse adt html和maven android
  • 在 C# 中向通用列表的 FindAll 添加参数

    我有一个要通过整数参数过滤的对象列表 List
  • 如何更改引导导航菜单颜色?

    我想更改 主页 旋转 社交媒体 的颜色 但不知道如何更改 我花了几天时间弄清楚如何更改导航背景 但对颜色一无所知 因为我只想将灰色更改为白色并更改悬停颜色 我可以将颜色更改为白色 但悬停颜色将被忽略 有没有一种预期的方法来做到这一点 这是我
  • 如果条件失败,Laravel 会从模型中抛出错误

    我正在模型中进行所有验证 我的规则是 public static rules array VehicleNumber gt required unique vehicle NumberSeats gt required VehicleTyp
  • 使用 AVMutableVideoComposition 导出时出现视频方向问题

    这是我用来导出视频的功能 void videoOutput 1 Early exit if there s no video file selected if self videoAsset UIAlertView alert UIAler
  • 声纳 C# 生态系统:fxcop 错误代码 521

    在构建 NET C 解决方案时 有以下日志 fxcop 配置中的详细开关 当单独执行 fxcopcmd exe 命令时 它工作得很好 为什么它不能与 sonar runner bat 一起使用 15 Sep 2011 03 05 37 DE
  • Pandas unstack 不起作用

    最初 我的 DF 包含 1 列使用 DatetimeIndex 索引的操作 In 371 dates 2013 12 29 19 21 00 action1 2013 12 29 19 21 01 action2 2013 12 29 19
  • MySQL PHP - 选择哪里 id = array()? [复制]

    这个问题在这里已经有答案了 可能的重复 MySQL 使用数组进行查询 https stackoverflow com questions 1101662 mysql query using an array 将数组传递给 mysql htt
  • 如何将 ActiveX 与 ASP.NET 结合使用

    我创建了一个 ActiveX 组件 但无法在 ASP NET 中访问该 ActiveX 组件 使用 JavaScript 创建 ActiveX 对象时 它给出 Microsoft JScript 运行时错误 自动化服务器无法创建对象 错误消
  • Android - 在视图页面中的不同片段之间导航时隐藏的 FAB

    我正在尝试做一些非常简单的事情 我希望 FAB 仅出现在 TabLayout 中的一个选项卡上 并在导航到另一选项卡时隐藏 例如 一个选项卡允许您在 FAB 中添加新项目 但下一个选项卡不允许您添加项目 我遵循 典型 XML 设计布局
  • 为什么*这个*不是*这个*?

    我刚刚编写了这段代码来表示这个正在杀死我的错误 Grrr 我想知道为什么当我得到错误 方法未定义我已经在 Safari 中检查过 parserDidStart 方法中的这个变量不是 EpisodeController 类型 而是 Episo
  • HTML 文件中的 javascript 调试

    如何在 javascript 函数的 HTML 页面中放置断点 以便在执行行时查看变量的值 这样做最简单的方法是什么 thanks 使用关键字 调试器 尝试调用硬断点 只要你的浏览器有JavaScript 调试启用 然后调试器 语句将告诉它
  • Spring Boot 设置 SSL 连接时出错

    我正在尝试将 Spring Boot 应用程序连接到 PostGresql 数据库 但每次我得到error in setting up SSL connection error My application properties文件如下 s
  • 检测 VBA 中是否选择了表单控件选项按钮

    我有一个使用 ActiveX 选项按钮可以正常工作的代码 但是 我希望宏也能在 Mac 上运行 因此我尝试用表单控件替换 ActiveX 控件 使用 ActiveX 为了检查是否选择了两个选项按钮之一 我所要做的就是 Sub OptionB
  • 第一次使用另一个画布作为源参数时,画布绘制图像速度较慢

    第一次使用另一个画布作为绘图源时 我看到画布绘制速度很慢 随后的 canvas 到 canvas drawImage 调用都很好 直到我交换图像 然后我再次看到相同的问题 下面的示例代码 加载图像 然后创建 4 个画布 第一个画布从图像本身