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 :
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).