phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
129 lines (99 loc) • 4.4 kB
JavaScript
/**
* @author Richard Davey <rich@phaser.io>
* @copyright 2013-2026 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var CanvasPool = require('../../display/canvas/CanvasPool');
var Color = require('../../display/color/Color');
var GetFastValue = require('../../utils/object/GetFastValue');
/**
* Takes a snapshot of an area from the current frame displayed by a WebGL canvas,
* or reads the color value of a single pixel, depending on the `getPixel` property
* of the snapshot configuration object.
*
* When capturing an area, the raw pixel data is read from the WebGL context and
* composited into a temporary canvas, accounting for WebGL's inverted Y-axis and
* optionally reversing pre-multiplied alpha. The canvas is then serialized to a
* data URL and loaded into an Image object, which is passed to the callback defined
* in the Snapshot Configuration object once loading completes.
*
* When reading a single pixel, the RGBA values at the given coordinates are read
* directly via `gl.readPixels` and returned as a `Phaser.Display.Color` object
* via the callback.
*
* @function Phaser.Renderer.Snapshot.WebGL
* @since 3.0.0
*
* @param {WebGLRenderingContext} sourceContext - The WebGL context to take a snapshot of.
* @param {Phaser.Types.Renderer.Snapshot.SnapshotState} config - The snapshot configuration object.
*/
var WebGLSnapshot = function (sourceContext, config)
{
var gl = sourceContext;
var callback = GetFastValue(config, 'callback');
var type = GetFastValue(config, 'type', 'image/png');
var encoderOptions = GetFastValue(config, 'encoder', 0.92);
var x = Math.abs(Math.round(GetFastValue(config, 'x', 0)));
var y = Math.abs(Math.round(GetFastValue(config, 'y', 0)));
var getPixel = GetFastValue(config, 'getPixel', false);
var isFramebuffer = GetFastValue(config, 'isFramebuffer', false);
var bufferWidth = (isFramebuffer) ? GetFastValue(config, 'bufferWidth', 1) : gl.drawingBufferWidth;
var bufferHeight = (isFramebuffer) ? GetFastValue(config, 'bufferHeight', 1) : gl.drawingBufferHeight;
if (getPixel)
{
var pixel = new Uint8Array(4);
var destY = bufferHeight - y - 1;
gl.readPixels(x, destY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
callback.call(null, new Color(pixel[0], pixel[1], pixel[2], pixel[3]));
}
else
{
var width = Math.floor(GetFastValue(config, 'width', bufferWidth));
var height = Math.floor(GetFastValue(config, 'height', bufferHeight));
var total = width * height * 4;
var pixels = new Uint8Array(total);
gl.readPixels(x, bufferHeight - y - height, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
var canvas = CanvasPool.createWebGL(this, width, height);
var ctx = canvas.getContext('2d', { willReadFrequently: true });
var imageData = ctx.getImageData(0, 0, width, height);
var data = imageData.data;
for (var py = 0; py < height; py++)
{
for (var px = 0; px < width; px++)
{
var sourceIndex = ((height - py - 1) * width + px) * 4;
var destIndex = (py * width + px) * 4;
var r = pixels[sourceIndex + 0];
var g = pixels[sourceIndex + 1];
var b = pixels[sourceIndex + 2];
var a = pixels[sourceIndex + 3];
// Un-premultiplication.
if (config.unpremultiplyAlpha && a !== 0)
{
var ratio = 255 / a;
r = Math.floor(r * ratio);
g = Math.floor(g * ratio);
b = Math.floor(b * ratio);
}
data[destIndex + 0] = r;
data[destIndex + 1] = g;
data[destIndex + 2] = b;
data[destIndex + 3] = a;
}
}
ctx.putImageData(imageData, 0, 0);
var image = new Image();
image.onerror = function ()
{
callback.call(null);
CanvasPool.remove(canvas);
};
image.onload = function ()
{
callback.call(null, image);
CanvasPool.remove(canvas);
};
image.src = canvas.toDataURL(type, encoderOptions);
}
};
module.exports = WebGLSnapshot;