图像操作 - 在精确位置添加带有角的图像 [英] Image Manipulation - add image with corners in exact positions

查看:207
本文介绍了图像操作 - 在精确位置添加带有角的图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一张图片,其中包含如下的盒装区域:

I have an image which is a background containing a boxed area like this:

我知道那个形状的角落的确切位置,我想在其中放置另一个图像。 (所以它似乎是在框内)。

I know the exact positions of the corners of that shape, and I'd like to place another image within it. (So it appears to be inside the box).

我知道drawImage方法HTML5画布,但它似乎只支持x,y,宽度,高度参数而不是精确坐标。

I'm aware of the drawImage method for HTML5 canvas, but it seems to only support x, y, width, height parameters rather than exact coordinates. How might I draw an image onto a canvas at a specific set of coordinates, and ideally have the browser itself handle stretching the image.

推荐答案

一种方法是使用四边形变换。

The example shown here is simplified and uses basic sub-divison and "cheats" on the rendering itself - that is, it draws in a small square instead of the shape of the sub-divided cell but because of the small size and the overlap we can get away with it in many non-extreme cases.

这里显示的示例是简化的,它使用基本的子图形, divison和骗子的渲染本身 - 也就是说,它绘制一个小正方形,而不是细分的单元格的形状,但因为小尺寸和重叠,我们可以在许多非极端情况下。

The proper way would be to split the shape into two triangles, then scan pixel wise in the destination bitmap, map the point from destination triangle to source triangle. If the position value was fractional you would use that to determine pixel interpolation (f.ex. bi-linear 2x2 or bi-cubic 4x4).

正确的方法是将形状拆分为两个三角形,然后在目标位图中逐像素扫描,将点从目标三角形映射到源三角形。如果位置值是分数,你将使用它来确定像素插值(f.ex.双线性2x2或双立方4x4)。

I do not intend to cover all this in this answer as it would quickly become out of scope for the SO format, but the method would probably be suitable in this case unless you need to animate it (it is not performant enough for that if you want high resolution).

我不打算在这个答案中覆盖所有这一切,因为它会迅速超出范围的SO格式,但该方法可能会适合在这种情况下,除非你需要动画它(如果你想要高分辨率,这是不够的性能)。

Lets start with an initial quadrilateral shape:

以一个初始的四边形形状开始:

第一步是对每个条C1-C4和C2-C3上的Y位置进行插值。我们需要当前位置以及下一个位置。我们将使用 t 的归一化值对此进行线性插值(lerp):

y1current = lerp( C1, C4, y / height) y2current = lerp( C2, C3, y / height) y1next = lerp(C1, C4, (y + step) / height) y2next = lerp(C2, C3, (y + step) / height)

This gives us a new line between and along the outer vertical bars.

$ b b

这给我们在外部垂直条之间和之间的新行。

接下来我们需要该行的X位置,当前和下一个。这将给我们四个位置,我们将填充当前像素,或者as-is或interpolate它(这里未显示):

p1 = lerp(y1current, y2current, x / width) p2 = lerp(y1current, y2current, (x + step) / width) p3 = lerp(y1next, y2next, (x + step) / width) p4 = lerp(y1next, y2next, x / width)

x and y will be the position in the source image using integer values.

x y 将是使用整数值的源图片中的位置。

演示程序可以通过一个循环来循环遍历源位图中的每个像素。发现在答案的底部。

The demo will have moire and other artifacts, but as mentioned earlier that would be a topic for another day.

演示具有波纹和

您还可以使用WebGL或Three.js来设置3D环境并将其渲染到画布上。这是指向后一个解决方案的链接:

You can also use WebGL or Three.js to setup a 3D environment and render to canvas. Here is a link to the latter solution:

使用纹理映射表面:

  • Three.js texturing (instead of defining a cube, just define one place/face).

使用这种方法可以将结果导出到画布或图像,但是为了性能,

Using this approach will enable you to export the result to a canvas or an image as well, but for performance a GPU is required on the client.

如果您不需要导出或操作结果,我建议使用简单的CSS 3D变换,如其他答案所示。

If you don't need to export or manipulate the result I would suggest to use simple CSS 3D transform as shown in the other answers.

/* Quadrilateral Transform - (c) Ken Nilsen, CC3.0-Attr */
var img = new Image();  img.onload = go;
img.src = "https://i.imgur.com/EWoZkZm.jpg";

function go() {
  var me = this,
      stepEl = document.querySelector("input"),
      stepTxt = document.querySelector("span"),
      c = document.querySelector("canvas"),
      ctx = c.getContext("2d"),
      corners = [
        {x: 100, y: 20},           // ul
        {x: 520, y: 20},           // ur
        {x: 520, y: 380},          // br
        {x: 100, y: 380}           // bl
      ],
      radius = 10, cPoint, timer,  // for mouse handling
      step = 4;                    // resolution

  update();

  // render image to quad using current settings
  function render() {
		
    var p1, p2, p3, p4, y1c, y2c, y1n, y2n,
        w = img.width - 1,         // -1 to give room for the "next" points
        h = img.height - 1;

    ctx.clearRect(0, 0, c.width, c.height);

    for(y = 0; y < h; y += step) {
      for(x = 0; x < w; x += step) {
        y1c = lerp(corners[0], corners[3],  y / h);
        y2c = lerp(corners[1], corners[2],  y / h);
        y1n = lerp(corners[0], corners[3], (y + step) / h);
        y2n = lerp(corners[1], corners[2], (y + step) / h);

        // corners of the new sub-divided cell p1 (ul) -> p2 (ur) -> p3 (br) -> p4 (bl)
        p1 = lerp(y1c, y2c,  x / w);
        p2 = lerp(y1c, y2c, (x + step) / w);
        p3 = lerp(y1n, y2n, (x + step) / w);
        p4 = lerp(y1n, y2n,  x / w);

        ctx.drawImage(img, x, y, step, step,  p1.x, p1.y, // get most coverage for w/h:
            Math.ceil(Math.max(step, Math.abs(p2.x - p1.x), Math.abs(p4.x - p3.x))) + 1,
            Math.ceil(Math.max(step, Math.abs(p1.y - p4.y), Math.abs(p2.y - p3.y))) + 1)
      }
    }
  }
  
  function lerp(p1, p2, t) {
    return {
      x: p1.x + (p2.x - p1.x) * t, 
      y: p1.y + (p2.y - p1.y) * t}
  }

  /* Stuff for demo: -----------------*/
  function drawCorners() {
    ctx.strokeStyle = "#09f"; 
    ctx.lineWidth = 2;
    ctx.beginPath();
    // border
    for(var i = 0, p; p = corners[i++];) ctx[i ? "lineTo" : "moveTo"](p.x, p.y);
    ctx.closePath();
    // circular handles
    for(i = 0; p = corners[i++];) {
      ctx.moveTo(p.x + radius, p.y); 
      ctx.arc(p.x, p.y, radius, 0, 6.28);
    }
    ctx.stroke()
  }
	
  function getXY(e) {
    var r = c.getBoundingClientRect();
    return {x: e.clientX - r.left, y: e.clientY - r.top}
  }
	
  function inCircle(p, pos) {
    var dx = pos.x - p.x,
        dy = pos.y - p.y;
    return dx*dx + dy*dy <= radius * radius
  }

  // handle mouse
  c.onmousedown = function(e) {
    var pos = getXY(e);
    for(var i = 0, p; p = corners[i++];) {if (inCircle(p, pos)) {cPoint = p; break}}
  }
  window.onmousemove = function(e) {
    if (cPoint) {
      var pos = getXY(e);
      cPoint.x = pos.x; cPoint.y = pos.y;
      cancelAnimationFrame(timer);
      timer = requestAnimationFrame(update.bind(me))
    }
  }
  window.onmouseup = function() {cPoint = null}
  
  stepEl.oninput = function() {
    stepTxt.innerHTML = (step = Math.pow(2, +this.value));
    update();
  }
  
  function update() {render(); drawCorners()}
}

body {margin:20px;font:16px sans-serif}
canvas {border:1px solid #000;margin-top:10px}

<label>Step: <input type=range min=0 max=5 value=2></label><span>4</span><br>
<canvas width=620 height=400></canvas>

这篇关于图像操作 - 在精确位置添加带有角的图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆