Search code examples
javascriptcanvashtml5-canvas

How to get a rotated crop of an image with HTML canvas


I have a large canvas containing an image, as shown in the example below:

enter image description here

I have the position and rotation angle of the red rectangle:

red : {
  top : top,
  left : left,
  width : width,
  height : height,
  angle : angle
}

I also have a full set of translated coordinates denoting the actual corner points of the red rotated rectangle.

Finally, I have the position of the blue rectangle relative to the red rectangle:

blue : {
  left : left,
  top : top,
  width : width,
  height : height
}

What I need to do is create a new canvas that is the size of the blue rectangle. The new canvas should contain the correctly rotated portion of the image that is contained within the blue rectangle. The resulting image should look like this:

enter image description here

Here is my JavaScript code so far:

var c = getCenterPoint(); // returns center x/y positions of the RED rectangle
canvas.width = blue.width;
canvas.height = blue.height;
var blueX = red.left + blue.left;
var blueY = red.top + blue.top;
var tx = blueX - c.x;
var ty = blueY - c.y;

this.cursorContext.translate(tx, ty);
this.cursorContext.rotate(angle * (Math.PI / 180));
this.cursorContext.translate(-tx, -ty);

this.cursorContext.drawImage(image, -blueX, -blueY, blue.width, blue.height);

The problem I am having is getting the correct portion of the image when the rectangle is rotated. How can I do this?


Solution

  • You can use a temporary canvas to clip and unrotate your blue box

    • Clip the boundingbox of the blue rectangle from the image

    • Unrotate the boundingbox so the blue rectangle is unrotated (angle==0)

    • Clip the extra boundingbox area away to reveal only the blue rectangle

    • Draw the blue rectangle to the display canvas

    enter image description here

    Here’s code and a Demo: http://jsfiddle.net/m1erickson/28EkG/

    <!doctype html>
    <html>
    <head>
    <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    
    <style>
        body{ background-color: ivory; }
        canvas{border:1px solid red;}
    </style>
    
    <script>
    $(function(){
    
        var canvas=document.getElementById("canvas");
        var ctx=canvas.getContext("2d");
    
        // blue rect's info
    
        var blueX=421;
        var blueY=343;
        var blueWidth=81;
        var blueHeight=44;
        var blueAngle=-25.00*Math.PI/180;
    
        // load the image
    
        var img=new Image();
        img.onload=start;
        img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/temp6.jpg";
    
        function start(){
    
            // create 2 temporary canvases
    
            var canvas1=document.createElement("canvas");
            var ctx1=canvas1.getContext("2d");
            var canvas2=document.createElement("canvas");
            var ctx2=canvas2.getContext("2d");
    
            // get the boundingbox of the rotated blue box
    
            var rectBB=getRotatedRectBB(blueX,blueY,blueWidth,blueHeight,blueAngle);
    
            // clip the boundingbox of the rotated blue rect
            // to a temporary canvas
    
            canvas1.width=canvas2.width=rectBB.width;
            canvas1.height=canvas2.height=rectBB.height;
    
            ctx1.drawImage(img,
                rectBB.cx-rectBB.width/2,
                rectBB.cy-rectBB.height/2,
                rectBB.width,
                rectBB.height,
                0,0,rectBB.width,rectBB.height
            );
    
            // unrotate the blue rect on the temporary canvas
    
            ctx2.translate(canvas1.width/2,canvas1.height/2);
            ctx2.rotate(-blueAngle);
            ctx2.drawImage(canvas1,-canvas1.width/2,-canvas1.height/2);
    
            // draw the blue rect to the display canvas
    
            var offX=rectBB.width/2-blueWidth/2;
            var offY=rectBB.height/2-blueHeight/2;
    
            canvas.width=blueWidth;
            canvas.height=blueHeight;
            ctx.drawImage(canvas2,-offX,-offY);
    
        }  // end start
    
    
    
        // Utility: get bounding box of rotated rectangle
    
        function getRotatedRectBB(x,y,width,height,rAngle){
            var absCos=Math.abs(Math.cos(rAngle));
            var absSin=Math.abs(Math.sin(rAngle));
            var cx=x+width/2*Math.cos(rAngle)-height/2*Math.sin(rAngle);
            var cy=y+width/2*Math.sin(rAngle)+height/2*Math.cos(rAngle); 
            var w=width*absCos+height*absSin;
            var h=width*absSin+height*absCos;
            return({cx:cx,cy:cy,width:w,height:h});
        }
    
    
    }); // end $(function(){});
    </script>
    
    </head>
    
    <body>
        <canvas id="canvas" width=300 height=300></canvas>
    </body>
    </html>