如何翻转 WebGLRenderingContext.readPixels() 的结果?
How can I flip the result of WebGLRenderingContext.readPixels()?
为什么我从WebGLRenderingContext.readPixels()
得到的imageData倒过来了?
我尝试执行以下操作:
var gl = renderer.domElement.getContext("webgl")
var pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4);
gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
var imageData = new ImageData(Uint8ClampedArray.from(pixels), gl.drawingBufferWidth, gl.drawingBufferHeight);
ctx.putImageData(imageData, 0, 0);
但结果是沿 x 轴镜像的图像(即:上下颠倒)。
我也试过在 ctx.putImageData
之后使用比例,如下所示:
ctx.scale(1, -1);
但是没有结果。反转像素也不起作用。
现在我明白 putImageData() 使用从左上角开始的坐标,而 readPixels() 从左下角开始。
有人对如何翻转图像或完全避免该问题有任何建议吗?
我不知道翻转 readPixels
的 webgl 方法,我怀疑确实有一种方法可以 "avoid the problem altogether",但是既然你似乎是在 2DContext 上绘制它,这里是翻转 putImageData
.
的方法
由于 putImageData
不受上下文转换的影响,因此简单地执行 ctx.scale(1,-1); ctx.putImageData()
是行不通的。
您需要先放置 ImageData,然后翻转其变换,然后再在其自身上绘制 canvas。
如果您有透明度,请使用 globalCompositeOperation = 'copy'
。
function flip(imageData){
var ctx = flipped.getContext('2d');
flipped.width = imageData.width;
flipped.height = imageData.height;
// first put the imageData
ctx.putImageData(imageData, 0,0);
// because we've got transparency
ctx.globalCompositeOperation = 'copy';
ctx.scale(1,-1); // Y flip
ctx.translate(0, -imageData.height); // so we can draw at 0,0
ctx.drawImage(flipped, 0,0);
// now we can restore the context to defaults if needed
ctx.setTransform(1,0,0,1,0,0);
ctx.globalCompositeOperation = 'source-over';
}
/* remaining is just for the demo */
var ctx = normal.getContext('2d');
var img = new Image();
img.crossOrigin = 'anonymous';
img.onload = getImageData;
img.src = "https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png";
function getImageData(){
normal.width = this.width;
normal.height = this.height;
ctx.drawImage(this, 0,0);
imageData = ctx.getImageData(0,0,this.width, this.height);
flip(imageData);
}
canvas{border: 1px solid}
body{background: ivory;}
<canvas id="normal"></canvas>
<canvas id="flipped"></canvas>
你可以像这样直接翻转ImadaData的像素。
const imageData = new ImageData(Uint8ClampedArray.from(pixels),gl.drawingBufferWidth,gl.drawingBufferHeight);
const w = imageData.width, h = imageData.height;
const data = imageData.data;
Array.from({length: h}, (val, i) => data.slice(i * w * 4, (i + 1) * w * 4))
.forEach((val, i) => data.set(val, (h - i - 1) * w * 4));
如果您要复制到 2d canvas 进行翻转,您不妨跳过 readPixels
。只需使用 drawImage
var dstX = 0;
var dstY = 0;
var dstWidth = ctx.canvas.width;
var dstHeight = ctx.canvas.height;
ctx.drawImage(gl.canvas, dstX, dstY, dstWidth, dstHeight);
浏览器将执行正确的操作,结果将正面朝上。
示例:
var gl = document.querySelector("#webgl").getContext("webgl");
var ctx = document.querySelector("#two_d").getContext("2d");
// fill webgl canvas with red on top and blue on bottom
gl.enable(gl.SCISSOR_TEST);
for (var y = 0; y < 15; ++y) {
var v = y / 14;
gl.scissor(0, y * 10, 300, 10);
gl.clearColor(v, 0, 1 - v, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
// copy it to 2d canvas
var dstX = 0;
var dstY = 0;
var dstWidth = ctx.canvas.width;
var dstHeight = ctx.canvas.height;
ctx.drawImage(gl.canvas, dstX, dstY, dstWidth, dstHeight);
canvas {
margin: 1em;
border: 1px solid black;
}
<canvas id="webgl"></canvas>
<canvas id="two_d"></canvas>
如果您确实出于某种原因确实想调用 gl.readPixels
(您无意将它们都放在 2d canvas 中,那么您可以翻转字节
var width = gl.drawingBufferWidth;
var height = gl.drawingBufferHeight
var pixels = new Uint8Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
var halfHeight = height / 2 | 0; // the | 0 keeps the result an int
var bytesPerRow = width * 4;
// make a temp buffer to hold one row
var temp = new Uint8Array(width * 4);
for (var y = 0; y < halfHeight; ++y) {
var topOffset = y * bytesPerRow;
var bottomOffset = (height - y - 1) * bytesPerRow;
// make copy of a row on the top half
temp.set(pixels.subarray(topOffset, topOffset + bytesPerRow));
// copy a row from the bottom half to the top
pixels.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
// copy the copy of the top half row to the bottom half
pixels.set(temp, bottomOffset);
}
示例:
var gl = document.querySelector("#webgl").getContext("webgl");
var ctx = document.querySelector("#two_d").getContext("2d");
// fill webgl canvas with red on top and blue on bottom
gl.enable(gl.SCISSOR_TEST);
for (var y = 0; y < 15; ++y) {
var v = y / 14;
gl.scissor(0, y * 10, 300, 10);
gl.clearColor(v, 0, 1 - v, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
var width = gl.drawingBufferWidth;
var height = gl.drawingBufferHeight
var pixels = new Uint8Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
var halfHeight = height / 2 | 0; // the | 0 keeps the result an int
var bytesPerRow = width * 4;
// make a temp buffer to hold one row
var temp = new Uint8Array(width * 4);
for (var y = 0; y < halfHeight; ++y) {
var topOffset = y * bytesPerRow;
var bottomOffset = (height - y - 1) * bytesPerRow;
// make copy of a row on the top half
temp.set(pixels.subarray(topOffset, topOffset + bytesPerRow));
// copy a row from the bottom half to the top
pixels.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
// copy the copy of the top half row to the bottom half
pixels.set(temp, bottomOffset);
}
// This part is not part of the answer. It's only here
// to show the code above worked
// copy the pixels in a 2d canvas to show it worked
var imgdata = new ImageData(with, height);
imgdata.data.set(pixels);
ctx.putImageData(imgdata, 0, 0);
canvas {
margin: 1em;
border: 1px solid black;
}
<canvas id="webgl"></canvas>
<canvas id="two_d"></canvas>
为什么我从WebGLRenderingContext.readPixels()
得到的imageData倒过来了?
我尝试执行以下操作:
var gl = renderer.domElement.getContext("webgl")
var pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4);
gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
var imageData = new ImageData(Uint8ClampedArray.from(pixels), gl.drawingBufferWidth, gl.drawingBufferHeight);
ctx.putImageData(imageData, 0, 0);
但结果是沿 x 轴镜像的图像(即:上下颠倒)。
我也试过在 ctx.putImageData
之后使用比例,如下所示:
ctx.scale(1, -1);
但是没有结果。反转像素也不起作用。
现在我明白 putImageData() 使用从左上角开始的坐标,而 readPixels() 从左下角开始。
有人对如何翻转图像或完全避免该问题有任何建议吗?
我不知道翻转 readPixels
的 webgl 方法,我怀疑确实有一种方法可以 "avoid the problem altogether",但是既然你似乎是在 2DContext 上绘制它,这里是翻转 putImageData
.
由于 putImageData
不受上下文转换的影响,因此简单地执行 ctx.scale(1,-1); ctx.putImageData()
是行不通的。
您需要先放置 ImageData,然后翻转其变换,然后再在其自身上绘制 canvas。
如果您有透明度,请使用 globalCompositeOperation = 'copy'
。
function flip(imageData){
var ctx = flipped.getContext('2d');
flipped.width = imageData.width;
flipped.height = imageData.height;
// first put the imageData
ctx.putImageData(imageData, 0,0);
// because we've got transparency
ctx.globalCompositeOperation = 'copy';
ctx.scale(1,-1); // Y flip
ctx.translate(0, -imageData.height); // so we can draw at 0,0
ctx.drawImage(flipped, 0,0);
// now we can restore the context to defaults if needed
ctx.setTransform(1,0,0,1,0,0);
ctx.globalCompositeOperation = 'source-over';
}
/* remaining is just for the demo */
var ctx = normal.getContext('2d');
var img = new Image();
img.crossOrigin = 'anonymous';
img.onload = getImageData;
img.src = "https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png";
function getImageData(){
normal.width = this.width;
normal.height = this.height;
ctx.drawImage(this, 0,0);
imageData = ctx.getImageData(0,0,this.width, this.height);
flip(imageData);
}
canvas{border: 1px solid}
body{background: ivory;}
<canvas id="normal"></canvas>
<canvas id="flipped"></canvas>
你可以像这样直接翻转ImadaData的像素。
const imageData = new ImageData(Uint8ClampedArray.from(pixels),gl.drawingBufferWidth,gl.drawingBufferHeight);
const w = imageData.width, h = imageData.height;
const data = imageData.data;
Array.from({length: h}, (val, i) => data.slice(i * w * 4, (i + 1) * w * 4))
.forEach((val, i) => data.set(val, (h - i - 1) * w * 4));
如果您要复制到 2d canvas 进行翻转,您不妨跳过 readPixels
。只需使用 drawImage
var dstX = 0;
var dstY = 0;
var dstWidth = ctx.canvas.width;
var dstHeight = ctx.canvas.height;
ctx.drawImage(gl.canvas, dstX, dstY, dstWidth, dstHeight);
浏览器将执行正确的操作,结果将正面朝上。
示例:
var gl = document.querySelector("#webgl").getContext("webgl");
var ctx = document.querySelector("#two_d").getContext("2d");
// fill webgl canvas with red on top and blue on bottom
gl.enable(gl.SCISSOR_TEST);
for (var y = 0; y < 15; ++y) {
var v = y / 14;
gl.scissor(0, y * 10, 300, 10);
gl.clearColor(v, 0, 1 - v, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
// copy it to 2d canvas
var dstX = 0;
var dstY = 0;
var dstWidth = ctx.canvas.width;
var dstHeight = ctx.canvas.height;
ctx.drawImage(gl.canvas, dstX, dstY, dstWidth, dstHeight);
canvas {
margin: 1em;
border: 1px solid black;
}
<canvas id="webgl"></canvas>
<canvas id="two_d"></canvas>
如果您确实出于某种原因确实想调用 gl.readPixels
(您无意将它们都放在 2d canvas 中,那么您可以翻转字节
var width = gl.drawingBufferWidth;
var height = gl.drawingBufferHeight
var pixels = new Uint8Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
var halfHeight = height / 2 | 0; // the | 0 keeps the result an int
var bytesPerRow = width * 4;
// make a temp buffer to hold one row
var temp = new Uint8Array(width * 4);
for (var y = 0; y < halfHeight; ++y) {
var topOffset = y * bytesPerRow;
var bottomOffset = (height - y - 1) * bytesPerRow;
// make copy of a row on the top half
temp.set(pixels.subarray(topOffset, topOffset + bytesPerRow));
// copy a row from the bottom half to the top
pixels.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
// copy the copy of the top half row to the bottom half
pixels.set(temp, bottomOffset);
}
示例:
var gl = document.querySelector("#webgl").getContext("webgl");
var ctx = document.querySelector("#two_d").getContext("2d");
// fill webgl canvas with red on top and blue on bottom
gl.enable(gl.SCISSOR_TEST);
for (var y = 0; y < 15; ++y) {
var v = y / 14;
gl.scissor(0, y * 10, 300, 10);
gl.clearColor(v, 0, 1 - v, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
var width = gl.drawingBufferWidth;
var height = gl.drawingBufferHeight
var pixels = new Uint8Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
var halfHeight = height / 2 | 0; // the | 0 keeps the result an int
var bytesPerRow = width * 4;
// make a temp buffer to hold one row
var temp = new Uint8Array(width * 4);
for (var y = 0; y < halfHeight; ++y) {
var topOffset = y * bytesPerRow;
var bottomOffset = (height - y - 1) * bytesPerRow;
// make copy of a row on the top half
temp.set(pixels.subarray(topOffset, topOffset + bytesPerRow));
// copy a row from the bottom half to the top
pixels.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
// copy the copy of the top half row to the bottom half
pixels.set(temp, bottomOffset);
}
// This part is not part of the answer. It's only here
// to show the code above worked
// copy the pixels in a 2d canvas to show it worked
var imgdata = new ImageData(with, height);
imgdata.data.set(pixels);
ctx.putImageData(imgdata, 0, 0);
canvas {
margin: 1em;
border: 1px solid black;
}
<canvas id="webgl"></canvas>
<canvas id="two_d"></canvas>