We develop complex WebGL powered application and offer our users an ability to save screenshot of the current scene. While this feature works as expected in Chrome and Firefox, we are facing several issues in safari.
Please take a look at the following code. As you can see, we use toDataURL() method of CanvasElement to make screenshots. While this code works as expected in other browsers, it produces wrong images in safari (at least for WebGL canvases).
You can easily reproduce the issue yourself by openning this file or running the attached code snippet in safari and other browsers.
Are we doing something wrong? Is there workaround for this issue?
Thank you.
UPD: Specially for those who say it is related to preserveDrawingBuffer i changed to attached code snippet to show that even with preserveDrawingBuffer set to true it still behaves wrong in safari.
<script type="text/javascript" src="http://www.html5rocks.com/en/tutorials/webgl/webgl_fundamentals/webgl/resources/webgl-utils.js"></script>
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}
</script>
<script id="2d-fragment-shader" type="x-shader/x-fragment">
void main() {
gl_FragColor = vec4(0,1,0,1); // green
}
</script>
<div>
<canvas id="canvas1" width="300" height="300"></canvas>
<img id="img1"/>
</div>
<div>
<canvas id="canvas2" width="300" height="300"></canvas>
<img id="img2"/>
</div>
<div>
<canvas id="canvas3" width="300" height="300"></canvas>
<img id="img3"/>
</div>
<div>
<canvas id="canvas4" width="300" height="300"></canvas>
<img id="img4"/>
</div>
<div>
<canvas id="canvas5" width="300" height="300"></canvas>
<img id="img5"/>
</div>
<div>
<canvas id="canvas6" width="300" height="300"></canvas>
<img id="img6"/>
</div>
<script type="text/javascript">
function test(canvas, img, premultipliedAlpha, alpha, preserveDrawingBuffer) {
var gl = canvas.getContext("experimental-webgl", {
premultipliedAlpha: premultipliedAlpha,
alpha: alpha,
preserveDrawingBuffer: preserveDrawingBuffer
});
var vertexShader = createShaderFromScriptElement(gl, "2d-vertex-shader");
var fragmentShader = createShaderFromScriptElement(gl, "2d-fragment-shader");
var program = createProgram(gl, [vertexShader, fragmentShader]);
gl.useProgram(program);
var positionLocation = gl.getAttribLocation(program, "a_position");
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, -1.0,
1.0, 1.0]),
gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, 5);
img.src = canvas.toDataURL();
}
window.onload = function() {
test(document.getElementById("canvas1"), document.getElementById("img1"), true, true, false);
test(document.getElementById("canvas2"), document.getElementById("img2"), false, true, false);
test(document.getElementById("canvas3"), document.getElementById("img3"), false, false, false);
test(document.getElementById("canvas4"), document.getElementById("img4"), true, true, true);
test(document.getElementById("canvas5"), document.getElementById("img5"), false, true, true);
test(document.getElementById("canvas6"), document.getElementById("img6"), false, false, true);
};
</script>
In Safari on Mac and iPhone, when
premultipliedAlpha: false
while creating awebgl
context,toDataURL
returns an image that is vertically flipped.This bug is still present in Safari in 2021. As a workaround, we can copy to a canvas with
2d
context, copy canvas data to that and calltoDataURL
.