Go Down

Topic: Rotating a bitmap (Read 7549 times) previous topic - next topic


Also consider any savings you might need to make to save memory and time

In my example I was only rotating circular objects, so the image would not actually have to be the full X*Y pixel size because as you rotate, chunks are sliced off of the corners

So I made savings as I only scanned the original image in the circular areas, and translated them to new points also within a circle

There are also other tricks I used with my example (its a con !).. What you see is actually half an image from the SD, but mirrored in XY (thus making two matching halves) the original BMPs were developped so that they matched when cropped then put back together in this way.. This saved half the ram for the original image, half the space on the SD and half the loading time for the image, half the maths calulations for new points because one original image pixel would then create TWO target pixels (mirror-mirror)

Also, naughty me, I was running at some ridiculous speed, like 32Mhz, but it was an ATMega328

I was also having to recreate PAL frames at 30fps, which nobbles about a great deal of the processor time

I can post some code, but to be honest it would look like ASM gobbledy-gook !! but it does work :)


One other thing... its important to decide which way you work the transformation

(a) Scan the original image array and create new target XY points from original XY points
(b) Go through the XY of target image array and pull the on/off pixel value from the original image using trig

I can tell you that using (a), the rotated image will have holes in it due to rounding errors etc, where as (b) will be pretty much perfect

Do not use sin/cos etc, use a progmem look up table (per degree). I used half sized tables (I think) as parts of each sin/cos table are repeated/refected. From memory I used an int_8 for one and a uint_8 for the other (cant remember which right now), since you are only using a low res image, you wont need the resolution of maths. Since I was doing this in assembler I just had to do a simple MUL/MULS/MULSU which only took one processor cycle

Anyway these are some of the thing I found to speed up calculations


Aug 25, 2016, 02:18 pm Last Edit: Aug 25, 2016, 04:01 pm by sk8jess
I hate to say it but advanced math is totally over my head right now. And here I am thinking this was going to be a simple project.
I'm honestly starting to thinking jumping onto an arduino mega or zero for more memory here. If i had more  memory I would just do a bunch of slightly rotated rotated images and call on them when needed. I'm starting to think that is going to be the best bet here.


Wow, i was completely overdoing this project. I just loaded 3 more of my bitmaps into my project and my memory has barely changed. I'm going to try and make individual bitmaps for everything I need here and just call them when needed.


I saw that earlier. Unfortunately that uses an old library that isn't supported on newer arduinos :\
but it should work with openGLCD.

--- bill


Here's some code that works on my 128x32 OLED.

Code: [Select]

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// These are the pins I use on a NodeMCU ESP-12E
//#define OLED_SDA D7 //MOSI
//#define OLED_SCL D5 //CLK
//#define OLED_DC  D4  //DC
//#define OLED_CS  12  // My OLED does no have a CS line, so just use a spare pin to keep library happy
//#define OLED_RST D3 //RES

// Software SPI on NodeMCU
//Adafruit_SSD1306 display(OLED_SDA, OLED_SCL, OLED_DC, OLED_RST, OLED_CS);

// Don't specify MOSI and SCLK so hardware SPI on NodeMCU
//Adafruit_SSD1306 display(OLED_DC, OLED_RST, OLED_CS);

// Using hardware SPI pins:
#define OLED_DIN   12
#define OLED_CLK   13
#define OLED_DC    10
#define OLED_CS    9
#define OLED_RESET -1

// Software bit bashed SPI

// Hardware SPI
//Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS);

#define DEG2RAD 0.0174532925

long unsigned int startMillis;
short unsigned int iter = 0;              // used to calculate the frames per second (FPS)
int angle = 0;

const static uint8_t PROGMEM testbmp1[] =
{ 16, 8,
  B00000001, B10000000,
  B00000010, B01000000,
  B00000100, B00100000,
  B00001000, B00010000,
  B00010000, B00001000,
  B00100000, B00000100,
  B01000000, B00000010,
  B10000000, B00000001

const static uint8_t PROGMEM testbmp2[] =
{ 16, 9,
  B00000011, B00000000,
  B00001111, B00000000,
  B00111111, B00000000,
  B01111111, B11111111,
  B11111111, B11111111,
  B01111111, B11111111,
  B00111111, B00000000,
  B00001111, B00000000,
  B00000011, B00000000

const static uint8_t PROGMEM bmp5x5[] =
{ 5, 5,
  B11111100, B01101011,
  B00011111, B10000000,

static const uint8_t PROGMEM adafruit[] =
{ 16, 16,
  B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000

#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");

void setup()   {

  // Need to hardware reset my display manually via pin 7
  digitalWrite(7, LOW);
  pinMode(7, OUTPUT);
  pinMode(7, INPUT);
  // by default, we'll generate the high voltage from the 3.3v line
  display.begin(SSD1306_SWITCHCAPVCC);  // initialize

void loop() {
  for (int angle = 0; angle < 360; angle += 6) {
    display.clearDisplay(); // Clear the display buffer

    drawRotatedBitmap(20, 16, testbmp2, angle);
    drawRotatedBitmap(64, 16, adafruit, angle);
    drawRotatedBitmap(108, 16, bmp5x5, angle);

    display.display();     // Now update the display with the buffer
    //delay(10); // Pause so we see it

void drawRotatedBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint16_t angle) {

  uint8_t w = pgm_read_byte(bitmap++);
  uint8_t h = pgm_read_byte(bitmap++);

  int16_t newx, newy;
  uint8_t data = 0;

  float  cosa = cos(angle * DEG2RAD), sina = sin(angle * DEG2RAD);

  x = x - ((w * cosa / 2) - (h * sina / 2));
  y = y - ((h * cosa / 2) + (w * sina / 2));

  for (int16_t j = 0; j < h; j++) {
    for (int16_t i = 0; i < w; i++ ) {
      if ((j * w + i) & 7) data <<= 1;
      else      data   = pgm_read_byte(bitmap++);

      newx = 0.5 + x + ((i * cosa) - (j * sina));
      newy = 0.5 + y + ((j * cosa) + (i * sina));

      if (data & 0x80) display.drawPixel(newx, newy, 1);
      //else            display.drawPixel(newx, newy, 0);

You will have to tweak it for your own display and interface.

The bitmaps are in Adafruit format with two extra start bytes that specify the x and y size.

As mcnobby say's you get digitisation errors with this approach.

On these small OLED screens you are typically better off using 2D or 3D vector graphics, a hacked example 3D vector plotting demo pulled off the web is attached.  Again you will have to tweak the sketch to suit your setup.


Wow bodmer, that's fantastic! great job. worked for me


4 years later and it worked for me with very few adjustments on my LED Matrix Panel, thanks



See previous post ##20 for code...

Go Up