以不规则形状裁剪图像并对其进行拉伸 [英] Crop the image in irregular shape and stretch it

查看:271
本文介绍了以不规则形状裁剪图像并对其进行拉伸的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现了描述我的问题的图片:





用户可以在画布上选择四个点来裁剪图像的一部分而不是拉伸它。



如何在HTML5中做到这一点? drawImage函数(正如我所知)仅适用于矩形(需要x,y,宽度和高度值),因此我无法使用不规则形状。该解决方案必须在每个现代浏览器中工作,所以我不希望基于webgl或其他东西。



编辑:
更多信息:这将是编辑图片的应用程序。我想让用户剪下一些更大的图片并编辑它。它将类似于Paint,因此需要画布来编辑像素。

解决方案

所以这里有一个杀戮技巧:你可以使用context2d的常规drawImage在三角形内绘制纹理。

约束是纹理坐标必须是轴对齐的。



要绘制纹理三角形,您必须:

•自己计算绘制图像所需的变换。

•剪切到一个三角形,因为drawImage会绘制一个四边形。

•drawImage用正确的变换。

所以我们的想法是将你的四边形分成两个三角形,并呈现两者。但是还有一个技巧:绘制右下角三角形时,纹理读取应该从纹理的右下角开始,然后向上和向左移动。这不能在Firefox中完成,它只接受drawImage的正面参数。所以我计算了第一个点与另外两个点的镜像,我可以再次以常规方向绘制 - 剪切将确保只绘制正确的部分 - 。



<小提琴在这里:




I found images that depict what is my problem:

User will able to choose four points on canvas to crop the part of image and than stretch it.

How to do that in HTML5? drawImage function (as I know) works only with rectangles (takes x, y, width and height values) so I can't use irregular shape. The solution have to work in every modern browser, so I don't want things based on webgl or something.

EDIT: More info: this will be app for editing pictures. I want to let user cut some part of bigger picture and edit that. It will be similar to Paint, so canvas is required to edit pixels.

解决方案

So here's a killing trick : You can use the regular drawImage of the context2d to draw a texture inside a triangle.
The constraint is that the texture coordinates must be axis-aligned.

To draw a textured triangle you have to :
• Compute by yourself the transform required to draw the image.
• Clip to a triangle, since drawImage would draw a quad.
• drawImage with the right transform.

So the idea is to split your quad into two triangles, and render both.

But there's one more trick : when drawing the lower-right triangle, texture reading should start from the lower-right part of the texture, then move up and left. This can't be done in Firefox, which accepts only positive arguments to drawImage. So i compute the 'mirror' of the first point vs the two others, and i can draw in the regular direction again -the clipping will ensure only right part is drawn-.

fiddle is here :

http://jsfiddle.net/gamealchemist/zch3gdrx/

function rasterizeTriangle(v1, v2, v3, mirror) {
    var fv1 = {
        x: 0,
        y: 0,
        u: 0,
        v: 0
    };
    fv1.x = v1.x;
    fv1.y = v1.y;
    fv1.u = v1.u;
    fv1.v = v1.v;
    ctx.save();
    // Clip to draw only the triangle
    ctx.beginPath();
    ctx.moveTo(v1.x, v1.y);
    ctx.lineTo(v2.x, v2.y);
    ctx.lineTo(v3.x, v3.y);
    ctx.clip();
    // compute mirror point and flip texture coordinates for lower-right triangle
    if (mirror) {
        fv1.x = fv1.x + (v3.x - v1.x) + (v2.x - v1.x);
        fv1.y = fv1.y + (v3.y - v1.y) + (v2.y - v1.y);
        fv1.u = v3.u;
        fv1.v = v2.v;
    }
    // 
    var angleX = Math.atan2(v2.y - fv1.y, v2.x - fv1.x);
    var angleY = Math.atan2(v3.y - fv1.y, v3.x - fv1.x);
    var scaleX = lengthP(fv1, v2);
    var scaleY = lengthP(fv1, v3);
    var cos = Math.cos,
        sin = Math.sin;
    // ----------------------------------------
    //     Transforms
    // ----------------------------------------
    // projection matrix (world relative to center => screen)
    var transfMatrix = [];
    transfMatrix[0] = cos(angleX) * scaleX;
    transfMatrix[1] = sin(angleX) * scaleX;
    transfMatrix[2] = cos(angleY) * scaleY;
    transfMatrix[3] = sin(angleY) * scaleY;
    transfMatrix[4] = fv1.x;
    transfMatrix[5] = fv1.y;
    ctx.setTransform.apply(ctx, transfMatrix);
    // !! draw !!
    ctx.drawImage(bunny, fv1.u, fv1.v, v2.u - fv1.u, v3.v - fv1.v,
    0, 0, 1, 1);
    //
    ctx.restore();
};

Edit : i added the relevant comment of @szym , with his example picture :

This only sort of looks right. If there are any straight lines in the original image you will see that each triangle is warped differently (2 different affine transforms rather than a perspective transform).

这篇关于以不规则形状裁剪图像并对其进行拉伸的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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