The first time I tried to rotate and scale a bitmap, I did the obvious choice: take each pixel from the source matrix, transform it with a rotation and scale, and write the value to the transformed location in the destination. The result looks like this:
bitmap = J( bmy = 200, bmx = 300, RGB Color( 1, 1, 1 ) );//white
scale = 3;
tranmat = (1 || 0 || 80) |/ (0 || 1 || 10) |/ (0 || 0 || 1); // 2D translation matrix
scalmat = (scale || 0 || 0) |/ (0 || scale || 0) |/ (0 || 0 || 1); // 2D scale matrix
angle = 33 * Pi() / 180;
sangle = Sin( angle );
cangle = Cos( angle );
rotamat = (cangle || -sangle || 0) |/ (sangle || cangle || 0) |/ (0 || 0 || 1); // 2D rotation matrix
forward = (tranmat * scalmat * rotamat)`; // transpose here
// make a sample image
testimage = Text Box( "matrix", <<SETWRAP></SETWRAP>
Transformed text has holesTransformed text has holes
Looks terrible. The correct way is to use the inverse of the transform matrix and ask "For each pixel in the destination bitmap, what pixel should I use from the source?" But the answer will almost always fall between four pixels. A reasonable answer is to use a weighted average of the four pixels, and more complicated answers might look like bi-cubic interpolation. This code uses a weighted average:
// this bitmap is used to build the image. drawtext will write
// each string into this bitmap.
bitmap = J( bmy = 300, bmx = 580, RGB Color( 1, 1, 1 ) );//white
// drawtext is a slow routine that draws a text string into a buffer,
// then samples that buffer to create a rotated, scaled copy in the
// bitmap buffer.
drawtext = Function(
{x = 0, y = 0, txt = "hello", degAngle = 0, xjust = 0, yjust = 1, fontsize = 99, scale = .5, textcolor = RGB Color( 0, 0, 0 )}, // parms
{// locals, initialized. Creates a transform matrix and its inverse
t = Text Box( txt, <<SETWRAP></SETWRAP>
Sampling the source image with linear interpolationSampling the source image with linear interpolation
The last half of the code is not so much about matrix transforms as making an interesting picture. The horizontal streaking is created by blending a number of copies of the image together, each shifted a pixel, and keeping less of the more distant shifts.
Big, tilted, orangeBig, tilted, orange