如果图像旋转,WebGL glfx.js矩阵变换(透视)会裁剪图像

时间:2017-01-24 09:32:59

标签: javascript canvas matrix webgl perspective

我正在使用glfx.js库,以便使用矩阵变换为我的图像创建透视效果。在我的应用程序中,系统的工作方式就像photoshop的智能对象(渲染平面图像并在渲染后获得透视效果)

glfx.js使用此函数canvas.perspective(before, after)将矩阵变换应用于图像,方法是在协调图像中的4个点之前和之后进行分配,然后在后台运行Matrix命令以转换图像。 / p>

我的问题是,如果应用转换后我想要的结果图像比原始图像大(如果旋转图像就会发生),那么WebGL画布将裁剪我的图像。

请看以下小提琴:

https://jsfiddle.net/human_a/o4yrheeq/

window.onload = function() {
    try {
      var canvas = fx.canvas();
    } catch (e) {
      alert(e);
      return;
    }

    // convert the image to a texture
    var image = document.getElementById('image');
    var texture = canvas.texture(image);

    // apply the perspective filter
    canvas.draw(texture).perspective( [0,0,774,0,0,1094,774,1094], [0,389,537,0,732,1034,1269,557] ).update();

    image.src = canvas.toDataURL('image/png');

    // or even if you replace the image with the canvas
    // image.parentNode.insertBefore(canvas, image);
    // image.parentNode.removeChild(image);
};
<script src="https://evanw.github.io/glfx.js/glfx.js"></script>
<img id="image" crossOrigin="anonymous" src="https://images.unsplash.com/photo-1485207801406-48c5ac7286b2?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=600&fit=max&s=9bb1a18da78ab0980d5e7870a236af88">

有关如何使WebGL画布适合旋转图像(不使图像变小)或以某种方式提取整个图像而不是裁剪的图像的任何想法?

1 个答案:

答案 0 :(得分:2)

更多像素

没有涵盖所有解决方案。这是因为当您从2D转换为3D时,投影图像的大小可能会无限大(近剪裁会阻止无穷大),因此无论您使用多大的图像输出,都始终可能会应用某些裁剪。

有了这个警告,大多数情况都可以避免削波。它非常简单,只需展开画布即可保存其他内容。

找到界限

为了简化计算,我将after数组更改为一组标准化点(它们表示后跟数作为图像大小的比例因子)。然后我使用图像大小转换为实际像素坐标。然后从我锻炼的最小尺寸纹理需要保持原始图像和投影。

使用该信息我只需创建纹理(作为画布)绘制图像。如果需要,调整befor数组(如果某些投影点在负空间中)并应用过滤器。

所以我们有一个宽度和高度的图像对象。你有这些观点的预测。

// assuming image has been loaded and is ready
var imgW = image.naturalWidth;
var imgH = image.naturalHeight;

设置角落阵列(之前)

var before = [0, 0, imgW, 0, 0, imgH, imgW, imgH];

投射点。为了更容易处理,我已将投影点标准化为图像大小

var projectNorm =  [[0, 0.3556], [0.6938, 0], [0.9457, 0.9452], [1.6395, 0.5091]];

如果要使用绝对坐标,如在小提琴后面的数组,请使用以下代码。之后的片段中的规范化相反,因此您可以跳过规范化。由于时间不够,我刚刚更新了答案。

var afterArray = [0,389,537,0,732,1034,1269,557];
projectNorm = [];
for(var i = 0; i < afterArray.length; i+= 2){
     afterArray.push([afterArray[i] / before[i], afterArray[i + 1] / before[i + 1]]);
}

现在计算投影的大小。这是重要的部分,因为它可以计算出画布的大小。

var top, left, right, bottom;
top = 0;
left = 0;
bottom = imgH;
right = imgW;
var project = projectNorm.map(p => [p[0] * imgW, p[1] * imgH]);
project.forEach(p => {
    top = Math.min(p[1], top);
    left = Math.min(p[0], left);
    bottom = Math.max(p[1], bottom);
    right = Math.max(p[0], right);
});

既然已经收集了我们需要的所有数据,我们可以创建一个可以容纳投影的新图像。 (假设投影点对投影是真的)

var texture = document.createElement("canvas");
var ctx = texture.getContext("2d");
texture.width = Math.ceil(right - left);
texture.height = Math.ceil(bottom - top);

在0,0

处绘制图像
ctx.setTransform(1, 0, 0, 1, left, top); // put origin so image is at 0,0
ctx.drawImage(image,0,0);
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform

然后展平投影点阵列

var after = [];
project.forEach(p => after.push(...p));

将所有点移动到正投影空间

after.forEach((p,i) => {
    if (i % 2) {
        before[i] += -top;
        after[i] += -top;
    } else {
        before[i] += -left;
        after[i] += -left;
    }
});

最后一步是创建glfx.js对象并应用过滤器

// create a fx canvas
var canvas = fx.canvas();
// create the texture 
var glfxTexture = canvas.texture(texture);
// apply the filter
canvas.draw(glfxTexture).perspective( before, after ).update();
// show the result on the page
document.body.appendChild(canvas);

演示

使用上述方法演示您的代码段(对图像加载稍作修改)

// To save time typing I have just kludged a simple load image wait poll
waitForLoaded();
function waitForLoaded(){
    if(image.complete){
        projectImage(image);
    }else{
        setTimeout(waitForLoaded,500);
    }
}
function projectImage(image){
    var imgW = image.naturalWidth;
    var imgH = image.naturalHeight;
    var projectNorm =  [[0, 0.3556], [0.6938, 0], [0.9457, 0.9452], [1.6395, 0.5091]];
    var before = [0, 0, imgW, 0, 0, imgH, imgW, imgH];
    var top, left, right, bottom;
    top = 0;
    left = 0;
    bottom = imgH;
    right = imgW;
    var project = projectNorm.map(p => [p[0] * imgW, p[1] * imgH]);
    project.forEach(p => {
        top = Math.min(p[1], top);
        left = Math.min(p[0], left);
        bottom = Math.max(p[1], bottom);
        right = Math.max(p[0], right);
    });
    var texture = document.createElement("canvas");
    var ctx = texture.getContext("2d");
    texture.width = Math.ceil(right - left);
    texture.height = Math.ceil(bottom - top);
    ctx.setTransform(1, 0, 0, 1, left, top); // put origin so image is at 0,0
    ctx.drawImage(image,0,0);
    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
    var after = [];
    project.forEach(p => after.push(...p));

    after.forEach((p,i) => {
        if (i % 2) {
           before[i] += -top;
           after[i] += -top;
        } else {
            before[i] += -left;
            after[i] += -left;
        }
    });
    // create a fx canvas
    var canvas = fx.canvas();
    // create the texture 
    var glfxTexture = canvas.texture(texture);
    // apply the filter
    canvas.draw(glfxTexture).perspective( before, after ).update();
    // show the result on the page
    document.body.appendChild(canvas);
}    
#image {
  display : none;
}
<script src="https://evanw.github.io/glfx.js/glfx.js"></script>
<img id="image" crossOrigin="anonymous" src="https://images.unsplash.com/photo-1485207801406-48c5ac7286b2?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=9bb1a18da78ab0980d5e7870a236af88">

备注和警告

  

注意投影点(在数组之后)并不总是与投影图像的最终角点匹配。如果发生这种情况,最终图像可能会被剪裁。

     

注意此方法仅在前点表示原始图像的外角时才有效。如果点(之前)在图像内,则此方法可能会失败。

     

警告无法审核生成的图像尺寸。大图像可能会导致浏览器变得迟缓,有时会崩溃。对于生产代码,您应该尽力将图像大小保持在使用代码的设备的限制范围内。客户很少返回缓慢和/或崩溃的页面。