Yep, transformation matrices is definitely the way to go. You want to start by defining a Matrix class that encapsulates a 2x2 matrix and defines certain operations. Most importantly, rotation matrix construction, matrix inversion, and vector multiplication. you can have mine for free (TODO: vector multiplication):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
class Matrix{
double matrix[4];
public:
Matrix(){
this->matrix[0]=
this->matrix[1]=
this->matrix[2]=
this->matrix[3]=0.0;
}
Matrix(double a,double b,double c,double d){
this->matrix[0]=a;
this->matrix[1]=b;
this->matrix[2]=c;
this->matrix[3]=d;
}
double determinant() const{
return this->matrix[0]*this->matrix[3]-this->matrix[1]*this->matrix[2];
}
Matrix inverse() const{
double a=1.0/this->determinant();
return Matrix(
a*this->matrix[3],
a*-this->matrix[1],
a*-this->matrix[2],
a*this->matrix[0]
);
}
Matrix operator*(const Matrix &m) const{
return Matrix(
this->matrix[0]*m.matrix[0]+this->matrix[1]*m.matrix[2],
this->matrix[0]*m.matrix[1]+this->matrix[1]*m.matrix[3],
this->matrix[2]*m.matrix[0]+this->matrix[3]*m.matrix[2],
this->matrix[2]*m.matrix[1]+this->matrix[3]*m.matrix[3]
);
}
const double &operator[](unsigned i)const{ return this->matrix[i]; }
static Matrix rotation(double alpha){
return Matrix(cos(alpha),-sin(alpha),sin(alpha),cos(alpha));
}
static Matrix scale(double x,double y){
return Matrix(x,0,0,y);
}
static Matrix shear(double x,double y){
return Matrix(1,x,y,1);
}
};
|
So, say you want to rotate a bitmap by theta radians. You'd start out by constructing your forward matrix:
|
Matrix forward_matrix = Matrix::rotation(theta);
|
The forward matrix corresponds to a linear transformation that rotates a 2D vector about the origin by theta radians. You can apply the linear transformation onto the vector by multiplying it by the matrix (v2 = M * v1).
Now you'l need to know how big the destination bitmap needs to be. That's fairly easy. Suppose you already have the corners of the source in four vectors:
You simply transform them with the forward_matrix:
1 2
|
for (int a = 0; a < 4; a++)
v = forward_matrix * v;
|
Now get the minima and maxima of each component for the four vectors. The difference between the minimum and maximum for a component is how large the destination needs to be along that axis.
A forward transformation would be inefficient. Instead, you'll want to be able to take a pixel in the destination bitmap and figure out which pixel(s) from the source to copy. For that, you need the backward matrix, which is the inverse of the forward matrix.
|
Matrix backward_matrix = forward_matrix.inverse();
|
Note in the code that it's not possible to invert a matrix whose determinant is zero. All rotation matrices have a non-zero determinant, though.
You now have everything you need to start copying pixels.
1 2 3 4 5 6 7 8 9
|
for (int y = 0; y < destination.height(); y++){
for (int x = 0; x < destination.width(); x++){
Vector destination_coord(x, y);
Vector source_coord = backward_matrix * destination_coord;
if (!source.bounding_rectangle.contains(source_coord))
continue;
destination.copy_onto(source.pixel(source_coord), destination_coord);
}
}
|