首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何从我不拥有的画布中获取像素数据?

如何从我不拥有的画布中获取像素数据?
EN

Stack Overflow用户
提问于 2019-01-04 23:46:17
回答 3查看 579关注 0票数 4

我正试图从画布中获取像素RGBA数据,以供进一步处理。我认为画布实际上是一个团结的游戏,如果这有区别的话。

我试着用摇摇与飞碟游戏的画布来做这件事。我使用来自readPixels上下文方法。

这就是我试过的:

代码语言:javascript
复制
var example = document.getElementById('#canvas');
var context = example.getContext('webgl2');      // Also doesn't work with: ', {preserveDrawingBuffer: true}'
var pixels = new Uint8Array(context.drawingBufferWidth * context.drawingBufferHeight * 4); 
context.readPixels(0, 0, context.drawingBufferWidth, context.drawingBufferHeight, context.RGBA, context.UNSIGNED_BYTE, pixels);

但所有像素显然都是黑色的(这显然不是真的)。

编辑:而且,我想要多次读取像素。谢谢大家的回答。https://stackoverflow.com/users/3702797/kaiido提供的答案对我来说非常有用:)

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-01-05 10:18:00

您只能要求画布上下文一次。如果将相同的选项传递给null,以下所有请求都将返回getContext(),或者返回之前创建的相同上下文。

现在,您链接到的一个页面在创建上下文时没有传递preserveDrawingBuffer选项,这意味着要从其中获取像素信息,您必须连接到与游戏循环发生的事件循环相同的事件循环。

幸运的是,这个精确的游戏确实使用了一个简单的requestAnimationFrame循环,因此要连接到同一个事件循环,我们所需要做的就是将代码打包到一个requestAnimationFrame调用中。

因为回调是堆叠的,而且它们确实需要从一个这样的回调的下一个帧创建一个循环,所以我们可以确保我们的调用会在它们之后被堆叠。

我现在意识到这可能并不明显,所以我将进一步解释requestAnimationFrame的功能,以及我们如何确保在联合的回调之后会被调用。

requestAnimationFrame(fn)fn回调推入一个回调堆栈中,这些回调将以先到先出的顺序同时被调用,就在浏览器执行屏幕操作之前。这偶尔发生一次(通常是每秒60次),在最近的事件循环结束时发生。

它可以理解为一种setTimeout(fn , time_remaining_until_next_paint),其主要区别是保证requestAnimationFrame回调执行器将在事件循环结束时被调用,从而在此事件循环执行其他js之后被调用。

因此,如果我们在调用回调的事件循环中调用requestAnimationFrame(fn),那么我们的假time_remaining_until_next_paint将是0,而fn将被推到堆栈的底部(最后一次进入,最后一次退出)。

当从回调执行器本身调用requestAnimationFrame(fn)时,time_remaining_until_next_paint将是16的一部分,而fn将在下一个帧的第一个调用中被调用。

因此,在请求动画框架的回调执行器之外进行的对requestAnimationFrame(fn)的任何调用都保证在与requestAnimationFrame驱动的循环相同的事件循环中调用,并在后面调用。

因此,我们需要获取这些像素,只需将对readPixels的调用包装在一个requestAnimationFrame调用中,并在统一的循环开始后将其称为

代码语言:javascript
复制
var example = document.getElementById('#canvas');
var context = example.getContext('webgl2') || example.getContext('webgl');
var pixels = new Uint8Array(context.drawingBufferWidth * context.drawingBufferHeight * 4);
requestAnimationFrame(() => {
  context.readPixels(0, 0, context.drawingBufferWidth, context.drawingBufferHeight, context.RGBA, context.UNSIGNED_BYTE, pixels);
  // here `pixels` has the correct data
});
票数 4
EN

Stack Overflow用户

发布于 2019-01-05 16:55:35

可能您需要在呈现的同一事件中读取像素,或者需要强制画布使用preserveDrawingBuffer: true,以便随时读取画布。

若要执行第二个覆盖getContext,请执行

代码语言:javascript
复制
HTMLCanvasElement.prototype.getContext = function(origFn) {
  const typesWeCareAbout = {
    "webgl": true,
    "webgl2": true,
    "experimental-webgl": true,
  };
  return function(type, attributes = {}) {
    if (typesWeCareAbout[type]) {
      attributes.preserveDrawingBuffer = true;
    }
    return origFn.call(this, type, attributes);
  };
}(HTMLCanvasElement.prototype.getContext);

在“团结”游戏之前将其放在文件的顶部,或者将其放在单独的脚本文件中,并在“团结”游戏之前将其包括在内。

现在,您应该能够在任何canvas Unity上获得上下文,并在任何时候调用gl.readPixels

对于另一种方法,即获取同一事件中的像素,您可以将requestAnimationFrame包装起来,以便在联合使用requestAnimationFrame之后插入您的requestAnimationFrame

代码语言:javascript
复制
window.requestAnimationFrame = function(origFn) {
  return function(callback) {
    return origFn(this, function(time) {
      callback(time);
      gl.readPixels(...);
    };
  };
}(window.requestAnimationFrame);

另一种解决方案是使用虚拟webgl上下文。此库展示了实现虚拟webgl上下文的示例。和shows 后处理单位输出的一个例子

请注意,在某个时候,联合可能会切换到使用OffscreenCanvas。到那时,它可能需要除上述解决方案之外的其他解决办法。

票数 3
EN

Stack Overflow用户

发布于 2021-05-08 06:43:31

或者,您可以将画布的内容流到一个视频元素中,将视频的内容绘制到另一个画布上,并读取那里的像素。

这应该独立于requestAnimationFrame绘制的框架,但是是异步的。

我们需要一个视频,另一个画布和一条溪流:

代码语言:javascript
复制
var example = document.getElementById('#canvas');
var stream=example.captureStream(0);//0 fps

var vid=document.createElement("video");
vid.width=example.width;
vid.height=example.height;
vid.style="display:none;";
document.body.appendChild(vid);

var canvas2=document.createElement("canvas");
canvas2.width=example.width;
canvas2.height=example.height;
canvas2.style="display:none;";
var width=example.width;
var height=example.height;
body.appendChild(canvas2);

var ctx = canvas2.getContext('2d');

现在,您可以通过请求流中的帧、将其插入视频并将视频绘制到我们的画布上来读取游戏画布:

代码语言:javascript
复制
stream.requestFrame();
//wait for the game to draw a frame
vid.srcObject=stream;
//wait
ctx.drawImage(vid, 0, 0, width, height, 0, 0, width, height);
var pixels = new Uint8Array(context.drawingBufferWidth * context.drawingBufferHeight * 4); 
ctx.readPixels(0, 0, ctx.drawingBufferWidth, ctx.drawingBufferHeight, ctx.RGBA, ctx.UNSIGNED_BYTE, pixels);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54047609

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档