Rotating a bitmap

Ah-ha. At least he does not want to use a Tiny25.

The 128x64 monochrome display is generally handled with a mirror-image in SRAM. i.e. 1024 bytes.
The UNO has only got 2048 bytes of SRAM. So you can not afford to have a second mirror.

Which means that you have to decide the amount of image to rotate. Calculate a new pixel. Save the old contents. Write the new pixel. Rinse and repeat for your "saved" pixel. Then update the OLED from the mirror.

This ends up as a lot of work. (if you want to keep the mirror containing the new image)

Alternatively, you take a pixel from the mirror, calculate the new position and write directly to the OLED. The mirror still contains the unrotated image.

i.e. one approach gives you a permanent 15 degree change. The other is better for animations where you do a continuous rotation.

Horizontal or Vertical scrolling is relatively easy to do with an OLED. Rotations look seriously difficult. It would be interesting to see how mcnobby has done it.

David.

bodmer:
Ignore the old library, it is the principles of bitmap manipulation and rotation function which can be adapted to your display.

Figure out how the "Rotate_and_Draw_Bitmap" function does the rotation with trigonometry (sine and cosine) and adapt the plotting to your library and you will have cracked the problem.

ah, as soon as i saw the old libraries i figured that's where everything was and moved on.
i'll dig back into it today!

INTP:
If you just want it rotated once to use it as that, do it in Photoshop and play with the bitmap programs for OLEDs.

too much memory for that, i would need a bunch of bitmaps, like 50+ and i'm already at 70% of memory used on my uno

mcnobby:
SmartShow ProjeX-SD with bitmap scale & rotation AVR DMX Arduino - YouTube

I wrote this for Arduino to read a bitmap from SD card and reproduce it on a PAL TV display, 200*200 pixels I think, also added full 360 rotation and zoom in/out, but it was written in assembler and a real bugger to get right

You want full rotation or just 90 degrees ?

i'll look into what you've done as well. Basically i'm going to have my bitmap rotate to x degree based on input. Could be 5*, could be 12*, could even be 45*. Baby steps though, I figured once I can get it rotated at least once I can look and manipulate it as I need

david_prentice:
Ah-ha. At least he does not want to use a Tiny25.

The 128x64 monochrome display is generally handled with a mirror-image in SRAM. i.e. 1024 bytes.
The UNO has only got 2048 bytes of SRAM. So you can not afford to have a second mirror.

Which means that you have to decide the amount of image to rotate. Calculate a new pixel. Save the old contents. Write the new pixel. Rinse and repeat for your "saved" pixel. Then update the OLED from the mirror.

This ends up as a lot of work. (if you want to keep the mirror containing the new image)

Alternatively, you take a pixel from the mirror, calculate the new position and write directly to the OLED. The mirror still contains the unrotated image.

i.e. one approach gives you a permanent 15 degree change. The other is better for animations where you do a continuous rotation.

Horizontal or Vertical scrolling is relatively easy to do with an OLED. Rotations look seriously difficult. It would be interesting to see how mcnobby has done it.

David.

I'm going to purchase an arduino mega eventually, i was just trying to get the basics done on this first. I can't believe how complicated image rotation is(coming from a web developer too though).

Have you thought about just attaching your OLED to a servo? :roll_eyes:

haha, that wouldn't work for this although it seems that would be easier than trying to figure this out!

Well, i'm making progress! Looking at the code from the linked forum post by @bodmer. I don't understand the calculations but i'm at least making progress. i've manually set the height and width of my bmp i'm using but it seems no matter what i set that or even the drawing bmp height and width it still turns out like the video below. actually if it goes over anything about 10x30 it seems to double the bmp instead of stretch it. my guess is it things it is in portrait instead of landscape?

sk8jess:
Before murdering me my knowledge and terminology is limited but I'm doing my best to learn.

I'm trying to rotate a bitmap on a 128x64 oled screen(0.96 Inch 4Pin White IIC I2C OLED Display Module 12864 LED Geekcreit for Arduino Sale - Banggood USA sold out-arrival notice) and i'm using the adafruit library for their ssd1306(GitHub - adafruit/Adafruit_SSD1306: Arduino library for SSD1306 monochrome 128x64 and 128x32 OLEDs) all powered by an Uno.
I can't seem to figure out how to rotate a bitmap but can only have it move horizontal or vertical on the x and y axis. I just want to rotate it let's say 15*. Can someone point me in the right direction here?

The key to your problem is rectangular to polar and polar to rectangular conversion.

Think of your bitmap image (a rectangle) sitting inside a circle. From the center of the circle to a pixel is the radius, and you use sine and cosine to determine the angle of the imaginary line from the center of the circle to the pixel.

Then, change that angle by the amount you want to rotate the image, convert the polar coordinates back to rectangular and draw the pixel at it's new location.

Repeat for each pixel and your image is rotated.

Of course, be warned... a 128 x 64 display has 8192 pixels... times all that floating point calculation... on an AVR... it's gonna be S-L-O-W.

And, you need at least 1K of free memory... you read the pixels from the display, determine their new locations, store them in ram, then re-draw the whole screen from ram.

Why 1K? Because there are 8 bits per byte... 8192 / 8 = 1024.

I've already tried this with a Noritake GU128X64-U100 vacuum florescent display (a 128 x 64 pixel display) and it literally takes almost 1/2 second to do an arbitrary rotation of the whole screen. Plus, the edges and/or corners get cut off (a portrait oriented bitmap cannot fit in a landscape oriented display!).

Have fun... take lots of aspirin. You'll need it.

INTP:
Have you thought about just attaching your OLED to a servo? :roll_eyes:

BWAHAAA!!! Too funny!

I'm already getting a headache from your post haha, not a good sign.

That being said, i'm taking a break for the night. I'm not making any more progress on my end.

If anyone is curious about my code it can be found here /*********************************************************************This is - Pastebin.com
The image I'm using is a square and it's 32x32

feel free to find the problem, ha.

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 :slight_smile:

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

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.

sk8jess:
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.

#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
Adafruit_SSD1306 display(OLED_DIN, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

// 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!");
#endif

void setup()   {

  // Need to hardware reset my display manually via pin 7
  digitalWrite(7, LOW);
  pinMode(7, OUTPUT);
  delay(200);
  pinMode(7, INPUT);
  delay(5);
  
  // 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.

Oled3d.zip (2.9 KB)

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

code please

See previous post ##20 for code...