blob: 2e01262147dfda0f4f6c1d574ec84c539f39750a [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>Media Capture from DOM Elements (canvas) Browser Test</title>
</head>
<body>
<div>Canvas capture Color Test.</div>
<canvas id='canvas-2d' width=10 height=10></canvas>
<video id='canvas-2d-local-view' autoplay></video>
<canvas id='canvas-webgl' width=10 height=10></canvas>
<video id='canvas-webgl-local-view' autoplay></video>
<canvas id='local-view-canvas' width=10 height=10></canvas>
<script type="text/javascript" src="mediarecorder_test_utils.js"></script>
<script>
'use strict';
const RED = [255, 0, 0, 1];
const GREEN = [0, 255, 0, 1];
const BLUE = [0, 0, 255, 1];
const RED_WITH_ALPHA = [255, 0, 0, 0.2];
const GREEN_WITH_ALPHA = [0, 255, 0, 0.5];
const BLUE_WITH_ALPHA = [0, 0, 255, 0.9];
const MAX_ALPHA = 255;
// The RGBA to YUV conversion is not perfectly reversible, so it is expected
// that there will be some color info lost when converting RGBA to YUV and then
// later YUV to RGBA.
const TOLERANCE = 10;
// This function draws a colored rectangle on the canvas.
function draw(canvasId, contextType, colorRgba, alphaContext) {
return new Promise(function(resolve, reject) {
// Wrapping the update in requestAnimationFrame is required for this to be
// a regression test for crbug.com/702446. requestAnimationFrame exposes
// this use case to a potential race between the frame capture and the
// frame clear that is caused by the {preserveDrawingBuffer: false} option
// on webgl contexts.
requestAnimationFrame(function() {
var context = canvasId.getContext(contextType, {alpha : alphaContext});
if (contextType == '2d') {
context.clearRect(0, 0, canvasId.clientWidth, canvasId.clientHeight);
context.fillStyle = 'rgba(' + colorRgba.join() + ')';
context.fillRect(0, 0, canvasId.clientWidth, canvasId.clientHeight);
} else {
context.clearColor(colorRgba[0] / 255, colorRgba[1] / 255, colorRgba[2] / 255, colorRgba[3]);
context.clear(context.COLOR_BUFFER_BIT);
}
resolve();
});
});
}
// This function gets all the pixels from the snapshot canvas.
// The snapshot canvas represents a snapshot of the video frame.
function getPixels(videoId, canvasId) {
var context = canvasId.getContext('2d');
context.clearRect(0, 0, canvasId.clientWidth, canvasId.clientHeight);
context.drawImage(videoId, 0, 0);
return context.getImageData(0, 0 , canvasId.clientWidth,
canvasId.clientHeight).data;
}
// This generator yields one pixel [R, G, B, A] from the supplied data.
function* getPixelGenerator(pixelData) {
for (var i = 0; i < pixelData.length; i += 4) {
yield pixelData.slice(i, i + 4);
}
}
// This function checks the color correctness of the whole video frame.
function isVideoColor(videoId, canvasId, colorRgba, alphaContext) {
var pixelIterator = getPixelGenerator(getPixels(videoId, canvasId));
for (var pixelRgba of pixelIterator) {
if (!isPixelColor(pixelRgba, colorRgba, alphaContext))
return false;
}
return true;
}
// This function checks the rgba color of a single pixel in the video.
function isPixelColor(pixel, color, alphaContext) {
var expectedColor = color.slice(0);
// The css rgba() function takes an alpha channel (a) such as 0 <= a <= 1,
// but context.getImageData() returns array of rgba data with alpha
// channel (a') such as 0 <= a' <= 255.
var checkLength = alphaContext ? color.length : color.length - 1;
expectedColor[expectedColor.length - 1] *= MAX_ALPHA;
for (var i = 0; i < checkLength; i++) {
if (Math.abs(pixel[i] - expectedColor[i]) > TOLERANCE) {
console.log('Expected ' + expectedColor + ', got ' + pixel);
return false;
}
}
return true;
}
function connectStream(contextType) {
var canvas = document.getElementById('canvas-' + contextType);
var video = document.getElementById('canvas-' + contextType + '-local-view');
var stream = canvas.captureStream();
video.src = URL.createObjectURL(stream);
video.load();
}
// This function runs the canvas capture rgba color checks.
function testCanvas2DCaptureColors(alphaContext) {
connectStream('2d');
doCanvasCaptureAndCheckRgba('2d', RED, alphaContext)
.then(function() {
return doCanvasCaptureAndCheckRgba('2d', GREEN, alphaContext);
})
.then(function() {
return doCanvasCaptureAndCheckRgba('2d', BLUE, alphaContext);
})
.then(function() {
return doCanvasCaptureAndCheckRgba('2d', RED_WITH_ALPHA, alphaContext);
})
.then(function() {
return doCanvasCaptureAndCheckRgba('2d', GREEN_WITH_ALPHA,
alphaContext);
})
.then(function() {
return doCanvasCaptureAndCheckRgba('2d', BLUE_WITH_ALPHA, alphaContext);
})
.catch(function(err) { window.domAutomationController.send(err); })
.then(function() { window.domAutomationController.send('OK'); });
}
function testCanvasWebGLCaptureColors(alphaContext) {
connectStream('webgl');
doCanvasCaptureAndCheckRgba('webgl', RED, alphaContext)
.then(function() {
return doCanvasCaptureAndCheckRgba('webgl', GREEN, alphaContext);
})
.then(function() {
return doCanvasCaptureAndCheckRgba('webgl', BLUE, alphaContext);
})
.then(function() {
return doCanvasCaptureAndCheckRgba('webgl', RED_WITH_ALPHA,
alphaContext);
})
.then(function() {
return doCanvasCaptureAndCheckRgba('webgl', GREEN_WITH_ALPHA,
alphaContext);
})
.then(function() {
return doCanvasCaptureAndCheckRgba('webgl', BLUE_WITH_ALPHA,
alphaContext);
})
.catch(function(err) { window.domAutomationController.send(err); })
.then(function() { window.domAutomationController.send('OK'); });
}
// Forces a context loss between captured frames and checks if capture continues
// as expected.
function testCanvas2DContextLoss(alphaContext) {
var contextType = '2d';
connectStream(contextType);
var canvas = document.getElementById('canvas-' + contextType);
doCanvasCaptureAndCheckRgba(contextType, RED, alphaContext)
.then(function() {
window.internals.loseSharedGraphicsContext3D();
// For the canvas to realize its Graphics context was lost we must try
// to use the contents of the canvas.
var imageData = canvas.getContext(contextType).getImageData(0, 0, 1, 1);
return doCanvasCaptureAndCheckRgba(contextType, BLUE, alphaContext);
})
.then(function() {
window.domAutomationController.send('OK');
}, function(err) {
window.domAutomationController.send(err);
});
}
// This function fills a canvas with one rgba color and canvas-captures it
// to a video element. We then snapshot the video element to
// another canvas and checks the color is what we expect.
function doCanvasCaptureAndCheckRgba(contextType, colorRgba, alphaContext) {
return new Promise(function(resolve, reject) {
var canvas = document.getElementById('canvas-' + contextType);
var video = document.getElementById('canvas-' + contextType + '-local-view');
var snapshotCanvas = document.getElementById('local-view-canvas');
draw(canvas, contextType, colorRgba, alphaContext)
.then(function() {
return waitFor('Verify the canvas color is as expected',
function() {
return isVideoColor(video, snapshotCanvas, colorRgba,
alphaContext);
});
})
.catch(function(err) {
reject(err);
})
.then(function() {
resolve();
});
});
}
</script>
</body>
</html>