Bitmap rotation

There isn’t a whole lot of information about manually rotating a bitmap image out there. It seems to be a very old practice, as most graphics libraries will do it for you so you don’t need to know how.

Anyway, I needed to know how for something I wanted to do on my Pokitto, as it is a bare-bones system, you have to do everything yourself (not strictly true, quite a lot has been done already).

The first thing most people will think of, is to cycle through each pixel in your source image, rotate it, then plot it on your screen.

This however, does not look very good at all. There will be gaps all over the place, as the pixels will not line up. So the way I did it, was the reverse. For every pixel in the destination image (screen) I un-rotated the pixel to find where it came from in the source image. This makes sure that every pixel is covered.

for(int y=0; y<height; y++){
    for(int x=0; x<width; x++){
        int x1 = (x-halfWidth)*TIMES; // multiply to avoid using floats
        int y1 = (y-halfHeight)*TIMES;
        int ux = (x1 * COS - y1 * SIN + HALFTIMES) / TIMES2;  // undo above multiplications to get x,y pixels
        int uy = (x1 * SIN + y1 * COS + HALFTIMES) / TIMES2;
        if(ux>-halfWidth && ux <halfWidth && uy >-halfHeight && uy <halfHeight){
            // grab pixel from 4bbp bitmap
            uint16_t i = (uy+halfHeight)*(width>>1) + (width-(ux+halfWidth)>>1);
            uint8_t pixel = image1[i+2];
            if (ux&1) col =  pixel & 0x0F;
            else col =  pixel>>4;

            mygame.display.drawPixel(x,y,col);
        }
    }
}

 

The following however, uses floating point math to copy the original image in straight lines at and a given angle. The output is uglier, but in theory it can be a lot faster, given that all of the multiplications of angles happens outside of the render loops.

/* Pokitto drawBitmap example - draws a Pokitto icon by @trelemar */

#include "Pokitto.h" // include Pokitto library
#include "pokitto_icon.h" // include the Pokitto icon graphics file

Pokitto::Core mygame; //create Pokitto application instance

float sine[361];
float cosine[361];

int main () {
    mygame.begin(); // start the application
    mygame.display.load565Palette(sprite_pal); //load the palette for the image
    mygame.display.bgcolor=0;

    float angle=0;
    for(int t=0; t<=360; t++){
        sine[t]=sin(angle);
        cosine[t]=cos(angle);
        angle+=(0.0174533); // radians
    }

    int dstW = mygame.display.width;
   int dstH = mygame.display.height;
    int srcW = sprite[0];
   int srcH = sprite[1];
    int fDstCX = dstW/2;
   int fDstCY = dstH/2;
    int fSrcCX = srcW/2;
   int fSrcCY = srcH/2;
    int fAngle = 0;
   float fScale = 2.5;

    int col;

    /* the "while" loop runs as long as the program is running */
    while (mygame.isRunning()) {

        /* mygame.update() is processed whenever it is time to update the screen */
        if (mygame.update()) {

        float SIN = sine[fAngle];  // pre-calculated look-up
        float COS = cosine[fAngle];

        float duCol = SIN * (1.0 / fScale);
        float dvCol = COS * (1.0 / fScale);
        float duRow = dvCol;
        float dvRow = -duCol;

        float startingu = fSrcCX - (fDstCX * dvCol + fDstCY * duCol);
        float startingv = fSrcCY - (fDstCX * dvRow + fDstCY * duRow);

        float rowu = startingu;
        float rowv = startingv;

            for(int y=0; y<dstH; y++){

                float u = rowu;
                float v = rowv;

                for(int x=0; x<dstW; x++){

                    if ( u >=0 && u < srcW && v >=0 && v < srcH ){
                        uint16_t i = int(v)*(srcW/2) + (srcW-int(u)>>1);
                        uint8_t pixel = sprite[i+2];
                        if (int(u)&1) col =  pixel & 0x0F;
                        else col =  pixel >> 4;

                        mygame.display.drawPixel(x,y,col);
                    }

                    u += duRow;
                    v += dvRow;

                }

                rowu = rowu + duCol;
                rowv = rowv + dvCol;

            }

//        if(ang++>=359) ang=0;

        fAngle-=1;
        if(fAngle<1) fAngle=359;

        }
    }

    return 0; // this is "good programming manners". Program informs it ended without errors
}

Floating point is slow however, I’ll come back at some point and try to convert it to fixed point, which is much much faster to calculate.