Javascript finds pixels when canvas rotates

I am trying to write rotated text (at different angles) on the canvas, but I do not want to overlap the texts. Therefore, after turning the canvas and before filling in the text, I tried to check the background text with measureText().widthand getImageData(), to see that there is no text there, to mess with the new one. I can not find the text (color pixels) while the canvas rotates. Here is a simplified version (using a rectangle) for my problem. I wonder why color pixels are not found?

<!DOCTYPE html>
<html>
<body>

<canvas id="myCanvas" width="300" height="150" style="border:1px solid black;">
Your browser does not support the HTML5 canvas tag.</canvas>

<script>

var cWidth=300, cHeight= 150;
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

// Rotate context around centre point
ctx.translate( cWidth/2, cHeight/2);
ctx.rotate(20 * Math.PI / 180);

// Draw 100x50px rectangle at centre of rotated ctx
ctx.fillStyle = "yellow";
ctx.fillRect(-50, -25, 100, 50);

// Is my rotated rectangle really there? 
// i.e. any coloured pixels at rotated cxt centre
imgData = ctx.getImageData(-50, -25, 100, 50);

// All rectangle pixels should be coloured
for (var j=0; j<imgData.data.length; j++){
	if (imgData.data[j] > 0){
		alert ("Coloured");
		break;
	};
};

// Why none is found?

</script>

</body>
</html>
Run codeHide result
The yellow rectangle should be in the same place and at an angle as the data area of ​​the test images. Something went wrong? How can I check the color of the rotating area? Being new to Javascript, I try to avoid libraries at this point.

Pekka

+4
3

, getImageData() canvas. x y translate(). :

// Translate the rectangle, rotate it and fill it 
ctx.translate(cWidth/2, cHeight/2);
ctx.rotate(20 * Math.PI / 180);
ctx.fillStyle = "yellow";
ctx.fillRect(-50, -25, 100, 50);

// Get the rectangle rotation
var imgData = ctx.getImageData(cWidth/2, cHeight/2, 100, 50);

JSfiddle . , !

0

- . .

:

imgData = ctx.getImageData(-50+cWidth/2,-25+cHeight/2,100,50);
+1

.

, 2D- javascript. . - , , . . , , . , , . , , .

API

var mMatrix = new Transform(); // creates a default matrix

// define the bounds 
const box = {x : -50, y : -25, w : 100, h : 50}; // our box


ctx.translate( cWidth/2, cHeight/2); // set the context transform
ctx.rotate(20 * Math.PI / 180);
// draw the stuff
ctx.fillStyle = "yellow";
ctx.fillRect(box.x, box.y, box.w, box.h);

// mirror the ctx transformations 
mMatrix.translate(cWidth/2, cHeight/2).rotate(20 * Math.PI / 180);
var ix,iy,x,y;
var v = new Transform.Vec(); // create a working vec to save memory usage and anyoning GC hits
for(iy = 0; iy < box.h; iy ++){ // for each vertical pixel
    for(ix = 0; iy < box.w; ix ++){  // for each horizontal
        v.x = ix + 0.5 + box.x; // use pixel center
        v.y = iy + 0.5 + box.y;  
        mMatrix.applyToVec(v); // transform into screen coords.
        v.x = Math.floor(v.x); // get rid of grogens
        v.y = Math.floor(v.y);

        // now we have the pixel address corresponding to the box coordinate ix,iy
        // get one pixel first check if it is on the canvas
        if(v.x >= 0 && v.x < ctx.canvas.width && v.y >= 0 && v.y < ctx.canvas.height){
            var pD = ctx.getImageData(v.x,v.y,1,1).data;
            var red = pD[0];
            var green = pD[1];
            var blue = pD[2];
            var alpha = pD[3];
            // now you have the RGB values
            ... do what ever you want with that info
        }
    }
}

Transform API

This is the conversion API needed to respond. This is short for the API I wrote, and you can do what you want (besides evil things). See Answer for use. These are just the basics, you can find a more complete Transform API on the web (but I don’t think you will find it much faster)

See the comment below for details on the method. Most chaining features.

The code snippet does not work.

var Transform = (function () {
    var tx, ty, v1, v2, v3, mat;
    // work vecs and transform provide pre assigned working memory
    v1 = new Vec();
    v2 = new Vec();
    v3 = new Vec();
    mat = new Transform();
    ty = tx = 0;
    
    function Transform(xAxisX, xAxisY, yAxisX, yAxisY, originX, originY) {
        if (xAxisX === undefined) { // create identity matrix
            this.xAxis = new Vec(); // Default vec is 1,0
            this.yAxis = new Vec(0, 1);
            this.origin = new Vec(0, 0);
        } else if (yAxisY === undefined) { // if only 3 arguments assume that the 3 arguments are vecs
            this.xAxis = new Vec(xAxisX.x, xAxisX.y); //
            this.yAxis = new Vec(xAxisY.x, xAxisY.y);
            this.origin = new Vec(yAxisY.x, xAxisY.y);
        } else {
            this.xAxis = new Vec(xAxisX, xAxisY); // Default vec is 1,0
            this.yAxis = new Vec(yAxisX, yAxisY);
            this.origin = new Vec(originX, originY);
        }
    };
    function Vec(x, y) {
        if (x === undefined || x === null) {
            this.x = 1;
            this.y = 0;
        } else {
            this.x = x;
            this.y = y;
        }
    };
    Vec.prototype = {
        copy : function () {
            return new Vec(this.x, this.y);
        },
        setAs : function (vec, y) { // set this to the value of vec, or if two arguments vec is x and y is y
            if (y !== undefined) {
                this.x = vec;
                this.y = y;
                return this;
            }
            this.x = vec.x;
            this.y = vec.y;
            return this;
        }
    }
    Transform.prototype = {
        xAxis : undefined,
        yAxis : undefined,
        origin : undefined,
        Vec : Vec, // expose the Vec interface
        copy : function () {
            return new Transform(this.xAxis, this.yAxis, this.origin);
        },
        setAs : function (transform) {
            this.xAxis.x = transform.xAxis.x;
            this.xAxis.y = transform.xAxis.y;
            this.yAxis.x = transform.yAxis.x;
            this.yAxis.y = transform.yAxis.y;
            this.origin.x = transform.origin.x;
            this.origin.y = transform.origin.y;
            return;
        },
        reset : function () { // resets this to the identity transform
            this.xAxis.x = 1;
            this.xAxis.y = 0;
            this.yAxis.x = 0;
            this.yAxis.y = 1;
            this.origin.x = 0;
            this.origin.y = 0;
            return this;
        },
        apply : function (x, y) { // returns an object {x : trabsformedX, y : trabsformedY} the returned object does not have the Vec prototype
            return {
                x : x * this.xAxis.x + y * this.yAxis.x + this.origin.x,
                y : x * this.xAxis.y + y * this.yAxis.y + this.origin.y
            };
        },
        applyToVec : function (vec) { // WARNING returns this not the vec.
            tx = vec.x * this.xAxis.x + vec.y * this.yAxis.x + this.origin.x;
            vec.y = vec.x * this.xAxis.y + vec.y * this.yAxis.y + this.origin.y;
            vec.x = tx;
            return this;
        },
        invert : function () { // inverts the transform
            // first check if just a scale translated identity matrix and invert that as it is quicker
            if (this.xAxis.y === 0 && this.yAxis.x === 0 && this.xAxis.x !== 0 && this.yAxis.y !== 0) {
                this.xAxis.x = 1 / this.xAxis.x;
                this.xAxis.y = 0;
                this.yAxis.x = 0;
                this.yAxis.y = 1 / this.yAxis.y;
                this.origin.x = -this.xAxis.x * this.origin.x;
                this.origin.y = -this.yAxis.y * this.origin.y;
                return this;
            }
            var cross = this.xAxis.x * this.yAxis.y - this.xAxis.y * this.yAxis.x;
            v1.x = this.yAxis.y / cross;
            v1.y = -this.xAxis.y / cross;
            v2.x = -this.yAxis.x / cross;
            v2.y = this.xAxis.x / cross;
            v3.x = (this.yAxis.x * this.origin.y - this.yAxis.y * this.origin.x) / cross;
            v3.y =  - (this.xAxis.x * this.origin.y - this.xAxis.y * this.origin.x) / cross;
            this.xAxis.x = v1.x;
            this.xAxis.y = v1.y;
            this.yAxis.x = v2.x;
            this.yAxis.y = v2.y;
            this.origin.x = v3.x;
            this.origin.y = v3.y;
            return this;
        },
        asInverse : function (transform) { // creates a new or uses supplied transform to return the inverse of this matrix
            if (transform === undefined) {
                transform = new Transform();
            }
            if (this.xAxis.y === 0 && this.yAxis.x === 0 && this.xAxis.x !== 0 && this.yAxis.y !== 0) {
                transform.xAxis.x = 1 / this.xAxis.x;
                transform.xAxis.y = 0;
                transform.yAxis.x = 0;
                transform.yAxis.y = 1 / this.yAxis.y;
                transform.origin.x = -transform.xAxis.x * this.origin.x;
                transform.origin.y = -transform.yAxis.y * this.origin.y;
                return transform;
            }
            var cross = this.xAxis.x * this.yAxis.y - this.xAxis.y * this.yAxis.x;
            transform.xAxis.x = this.yAxis.y / cross;
            transform.xAxis.y = -this.xAxis.y / cross;
            transform.yAxis.x = -this.yAxis.x / cross;
            transform.yAxis.y = this.xAxis.x / cross;
            transform.origin.x = (this.yAxis.x * this.origin.y - this.yAxis.y * this.origin.x) / cross;
            transform.origin.y =  - (this.xAxis.x * this.origin.y - this.xAxis.y * this.origin.x) / cross;
            return transform;
        },
        multiply : function (transform) { // multiplies this with transform
            var tt = transform;
            var t = this;
            v1.x = tt.xAxis.x * t.xAxis.x + tt.yAxis.x * t.xAxis.y;
            v1.y = tt.xAxis.y * t.xAxis.x + tt.yAxis.y * t.xAxis.y;
            v2.x = tt.xAxis.x * t.yAxis.x + tt.yAxis.x * t.yAxis.y;
            v2.y = tt.xAxis.y * t.yAxis.x + tt.yAxis.y * t.yAxis.y;
            v3.x = tt.xAxis.x * t.origin.x + tt.yAxis.x * t.origin.y + tt.origin.x;
            v3.y = tt.xAxis.y * t.origin.x + tt.yAxis.y * t.origin.y + tt.origin.y;
            t.xAxis.x = v1.x;
            t.xAxis.y = v1.y;
            t.yAxis.x = v2.x;
            t.yAxis.y = v2.y;
            t.origin.x = v3.x;
            t.origin.y = v3.y;
            return this;
        },
        rotate : function (angle) { // Multiply matrix by rotation matrix at angle
            var xdx = Math.cos(angle);
            var xdy = Math.sin(angle);
            v1.x = xdx * this.xAxis.x + (-xdy) * this.xAxis.y;
            v1.y = xdy * this.xAxis.x + xdx * this.xAxis.y;
            v2.x = xdx * this.yAxis.x + (-xdy) * this.yAxis.y;
            v2.y = xdy * this.yAxis.x + xdx * this.yAxis.y;
            v3.x = xdx * this.origin.x + (-xdy) * this.origin.y;
            v3.y = xdy * this.origin.x + xdx * this.origin.y;
            this.xAxis.x = v1.x;
            this.xAxis.y = v1.y;
            this.yAxis.x = v2.x;
            this.yAxis.y = v2.y;
            this.origin.x = v3.x;
            this.origin.y = v3.y;
            return this;
        },
        scale : function (scaleX, scaleY) { // Multiply the matrix by scaleX and scaleY
            this.xAxis.x *= scaleX;
            this.xAxis.y *= scaleY;
            this.yAxis.x *= scaleX;
            this.yAxis.y *= scaleY;
            this.origin.x *= scaleX;
            this.origin.y *= scaleY;
            return this;
        },
        translate : function (x, y) { // Multiply the matrix by translate Matrix
            this.origin.x += x;
            this.origin.y += y;
            return this;
        },
        setTransform : function (xAxisX, xAxisY, yAxisX, yAxisY, originX, originY) {
            this.xAxis.x = xAxisX;
            this.xAxis.y = xAxisY;
            this.yAxis.x = yAxisX;
            this.yAxis.y = yAxisY;
            this.origin.x = originX;
            this.origin.y = originY;
            return this;
        },
        transform : function (xAxisX, xAxisY, yAxisX, yAxisY, originX, originY) {
            var t = this;
            v1.x = xAxisX * t.xAxis.x + yAxisX * t.xAxis.x;
            v1.y = xAxisY * t.xAxis.x + yAxisY * t.xAxis.y;
            v2.x = xAxisX * t.yAxis.x + yAxisX * t.yAxis.x;
            v2.y = xAxisY * t.yAxis.x + yAxisY * t.yAxis.y;
            v3.x = xAxisX * t.origin.x + yAxisX * t.origin.y + originX;
            v3.y = xAxisY * t.origin.x + yAxisY * t.origin.y + originY;
            t.xAxis.x = v1.x;
            t.xAxis.y = v1.y;
            t.yAxis.x = v2.x;
            t.yAxis.y = v2.y;
            t.origin.x = v3.x;
            t.origin.y = v3.y;
            return this;
        },
        contextTransform : function (ctx) {
            ctx.transform(this.xAxis.x, this.xAxis.y, this.yAxis.x, this.yAxis.y, this.origin.x, this.origin.y);
            return this;
        },
        contextSetTransform : function (ctx) {
            ctx.Settransform(this.xAxis.x, this.xAxis.y, this.yAxis.x, this.yAxis.y, this.origin.x, this.origin.y);
            return this;
        },
        setFromCurrentContext : function(ctx){
            if(ctx && typeof ctx.currentTransform === "object"){
                var mat = ctx.currentTransform;
                this.xAxis.x = mat.a;
                this.xAxis.y = mat.b;
                this.yAxis.x = mat.c;
                this.yAxis.y = mat.d;
                this.origin.x = mat.e;
                this.origin.y = mat.f;
            }
            return this;
        }
            
    }
    if(typeof document.createElement("canvas").getContext("2d").currentTransform !== "object"){
        Transform.prototype.setFromCurrentContext = undefined;
    }
    return Transform;
})();


/*
rotate(angle) // Multiply matrix by rotation matrix at angle. Same as ctx.rotate
scale(scaleX, scaleY) // Multiply the matrix by scaleX and scaleY. Same as ctx.scale
translate(x, y) // Multiply the matrix by translate Matrix. Same as ctx.translate
setTransform(xAxisX, xAxisY, yAxisX, yAxisY, originX, originY)  //Replaces the current reansform with the new values. Same as ctx.setTransform
transform(xAxisX, xAxisY, yAxisX, yAxisY, originX, originY) // multiplies this transform with the supplied transform. Same as ctx.transform 


Transform.xAxis  // Vec object defining the direction and scale of the x Axis. Values are in canvas pixel coordinates
Transform.yAxis  // Vec object defining the direction and scale of the y Axis. Values are in canvas pixel coordinates
Transform.origin  // Vec object defining canvas pixel coordinates of the origin
Transform.Vec // interface to a vec object with basic interface needed to support Transform
Transform.reset() // resets the transform to the identity matrix (the default matrix used by 2D context)
Transform.copy() // creates a new copy of this object 
Transform.setAs(transform)  // sets the content of this to the values of the argument transform
Transform.apply(x, y) { // Transforms the coords x,y by multiplying them with this. Returns an object {x : trabsformedX, y : trabsformedY} the returned object does not have the Vec prototype
Transform.applyToVec(vec) // transforms the point vec. WARNING returns this not the vec.
Transform.invert() // inverts the transform
Transform.asInverse(transform) // creates a new or uses supplied transform to return the inverse of this matrix
Transform.multiply(transform) // multiplies this with transform
Transform.contextTransform(ctx) // multiplies the supplied context (ctx) transform by this.
Transform.contextSetTransform(ctx) // set the supplied context (ctx) transform to this
Transform.setFromCurrentContext(ctx)  // Only for supported browser. Sets this to the supplied context current transformation. May not be available if there is no browser support

 
There is also access to the very simple Vec object. To create a vec `new Transform.Vec(x,y)`


*/
Run codeHide result
0
source

All Articles