## Rotate Image on TFT Display by any angle

28 posts / 0 new
Author
Message

Hello. I am trying to rotate an image by any angle in embedded c and I am stuck on how to move forward. The current code I have is the following:

```  /* Calculate Trig Paramters for Rotation */
float tmpCos = cos(rot), tmpSin = sin(rot);

/* For each point */
for(int bmp_row = 0; bmp_row < 29; bmp_row++)
for(int bmp_col = 0; bmp_col < 29; bmp_col++)

/* Push color if point is '1' at rotated position*/
if(arrow_BMP[bmp_row][bmp_col] == 1)
ili9341_drawPixel(x + bmp_row*tmpCos - bmp_col*tmpSin, y + bmp_row*tmpSin + bmp_col*tmpCos, color);	```

The image is rotating by the correct angle, but the pivot point is not the center. Furthermore, some pixels are not filled in, which I assume is due to integer cutoff from cos/sin calculation.

Can someone please tell me if there is a better way to rotate an image by any angle in embedded c or how to fix these problems?

Hello there.

Personally I would pull the open source of programs that are able to do this (things like Gimp and Imagemagick) and see how they achieve it.

EDIT BTW assuming you probably aren't the very first to want to do this I just googled "image rotation algorithm" and one of the top hits was:

https://homepages.inf.ed.ac.uk/r...

which contains:

in which x2,y2 are the new location, x1,y1 are the old location and x0,y0 is the centre of rotation.

This seems to contain info about the centre of rotation that I'm not seeing in your simpler formulae. Presumably if you have something like a 320x240 display you want the centre of rotation at 160x120 perhaps?

Last Edited: Fri. Jul 20, 2018 - 03:22 PM

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

clawson wrote:
Personally I would pull the open source of programs that are able to do this (things like Gimp and Imagemagick) and see how they achieve it.

And/or, you could consider going old-school and studying the topic area.  The Foley/Van Dam selections may well be the starting point, as they were 30 years ago.

https://www.amazon.com/Computer-...

Computer Graphics: Principles and Practice, Third Edition, remains the most authoritative introduction to the field. The first edition, the original “Foley and van Dam,” helped to define computer graphics and how it could be taught. The second edition became an even more comprehensive resource for practitioners and students alike. This third edition has been completel ...

As you seem to be using C:

https://www.amazon.com/Computer-...

Computer Graphics: Principles and Practice in C (2nd Edition) 2nd Edition

by James D. Foley (Author), Andries van Dam (Author), Steven K. Feiner (Author), John F. Hughes (Author)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Rotating an image can be done with "shearing" one way followed by another shear.

It is expensive and painful.   It is always worth thinking how you can avoid this sort of operation.

If it is a simple graphic e.g. an arrow there are tricks.

If you are rotating a photograph it has to be done perfectly.   You have no escape.

David.

clawson wrote:
assuming you probably aren't the very first to want to do this

Further to that, ISTR finding that "Affine Transform" would be a good search term ...

Top Tips:

1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...

Thank you all for you references and suggestions. What I am trying to do now is make a function that rotates bitmap precisely. Speed is critical, but I do not have space for storing too many bitmaps, so I need an algorithm.

This is the 29x29 arrow bitmap:

{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0},

{0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0},

{0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},

{0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},

{0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},

{0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0},

{0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0},

{0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}

Here is gimp image:

Essentially need fast, precise rotation. A function that rotates smallest degree clockwise or counterclockwise. You guys think there is any trick or should I just use generic rotation algorithm?

Hello there.

Make the binary bit map be 32x32 instead of 29x29 so that you can store 8 pixels in one byte. Now your image is 128 (32 rows x 4 bytes per row) bytes in size.

Since your image is an arrow perhaps it would be easier to just make 16 images of 128 bytes each with the arrow point in 16 directions.  Then when the arrow changes, paste a new updated arrow direction image over the old one.  Calculating the image change by doing complicated math on the pixels is only going to slow down your program.  And at 29x29 pixels the arrow is going to be too small to see the small one or two pixel changes in direction.

Thanks for your input. The thing is, 29 x 29 is good size cause this is 2.2" TFT display. The arrow is meant to point to a certain area of screen (which could be anywhere), so 16 wont be enough. I like the idea of storing as bytes better, but still need an algorithm.

Hello there.

There's no FPU, so floating point math is going to be insanely bad. If you can possibly arrange to just store multiple precomputed images, you should. You can do 4x as many if you take advantage of horizontal/vertical flips, so 16 images would handle 64 different arrow orientations. If you store each image internally as 4*29 bytes, and just ignore the last three bits, you're down to 116 bytes per image, so 1,856 bytes giving you 64 distinct orientations, which is probably going to be just as good. And actually, you don't even need the full 29x29; in your image, there's five columns and eight rows that are all zeroes, and you can certainly drop at least some of those rows generically once you're using flipping. Draw a circle around the center of the arrow and through the point of the arrow. How many distinct pixel locations do you actually hit between the right-pointing arrow and just before the up-pointing arrow? I think 16 is enough, that covers every point the arrow can point to.

So given that image, if you wanted to draw it pointing up, just do a row/column inversion. If you wanted to draw it pointing left, count columns backwards (and count rows backwards, and then an arrow that would have been pointing slightly-above-right will point slightly-below-left). And so on.

Expect the actual computation, if done on the device, to take quite a long time.

Thanks. I suppose doing floating point math would take too long. I''ll find a nice place between storage and processing power.

One more thing. I'm doing this on a TFT 2.2 Hitlego 320X240 Display with SD Card slot. Is using an sd card a viable option for an arrow that should be placed fast, or would reading take too long?

Hello there.

What do you actually want to do?

Spinning a monochrome blue arrow on a circular grey background?  On a black screen.

Or moving an arrow transparently over a a photographic background?

Seriously,  your arrow is only going to alter slightly for every 10 degree angle.    You only need to plot pixels that XOR with the previous pixel.

i.e. changed pixels.

In terms of storage,  36 bitmaps of 32x32 pixels use 4608 bytes of Flash memory.   And possible 128 bytes of RAM to store the current image.   Or one byte of RAM to hold an index into the current set of images.

David.

Edit.   My maths was total rubbish.   Corrected figures in red.  A 32x32 bitmap is 32 bits per scanline.  32 scanlines.  i.e. 32 uint32_t (128 bytes).   A 29x29 bitmap would use 29 uint32_t.   Or a bit less if you pack all the bits.

Last Edited: Sun. Jul 22, 2018 - 08:07 AM

Ok ill go storage route. Does anyone know a tool for getting these byte arrays fast?

Hello there.

thearabianguy wrote:

Ok ill go storage route. Does anyone know a tool for getting these byte arrays fast?

Can you not just store it in your flash?

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

Yeah, im going to store in flash. However, do I need to manually find the corresponding byte arrays for each rotated image or is there something easier I can do?

Hello there.

You could probably write a thing on some other computer that does the computation and produces the byte arrays, but I would suggest at least looking at them once they're generated, and possibly hand-editing to smooth the edges better, because rotating images smoothly is very hard, and when you're doing single-bit images, you can't rely on antialiasing to improve them.

If you wish to display an arrow at an arbitrary rotation why not draw it (and fill) from first principles. looking at your bitmap you'll have to calculate new positions for the 7 points of your polygon, draw an outline using a lineto function perhaps; then filling.

It may actually be quicker than calculating pixel positions for an entire bitmap.

It may also give a cleaner looking image especially if you're overlaying the arrow over a background image.

After thinking about filling for a moment to two; that's probably a subject for a thread of its own. Or a chapter of it's own in one of the suggested publications.

Last Edited: Sat. Jul 21, 2018 - 09:06 PM

You only have to draw a filled circle.   Then draw two filled triangles.    You can calculate the coordinates with trig.

I am idle.   So I would let a graphics library do the drawing and then read the GRAM memory to create the bitmap.     Eventually store the bitmap in Flash.    (I would generate the C statements on the Serial Terminal)

Mind you,   you could calculate your own points on the filled triangle.   And write the bitmap from there.

Please confirm that this is a 29x29 image on a 240x320 TFT screen.   The image rotates around a fixed axis.   You do not have to worry about a complex background because your grey circle is opaque.

Is 10 degrees resolution ok?  i.e. 36 different angle images.

Do you want it slow enough that you can see it move?

David.

Last Edited: Sat. Jul 21, 2018 - 09:50 PM

Best way to rotate is to select the point to be filled in. Then calculate the point in the original image to copy this point to its new position.

EDIT: Never mind I just added a check to see if calculated values were in bound and now everything seems to work fine.

Ideally want to see the arrow move smoothly. Im just whipping up some code right now to get the byte arrays of the rotated images and just copy and paste them into AVR code. I am having a bit of trouble getting the rotation to work, though. Maybe you guys can help.

Here is the code to generate rotated image and code:

```	/* Rotate BMP */
for (int x = 0; x < BMPSIZE; x++)
for (int y = 0; y < BMPSIZE; y++) {

cout << "x,y = " << x << ',' << y << "...";

int xr = ((x - BMPCENTER) * cos(rad) - (y - BMPCENTER) * sin(rad)) + BMPCENTER;
int yr = ((x - BMPCENTER) * sin(rad) + (y - BMPCENTER) * cos(rad)) + BMPCENTER;

//		newBMP[xr][yr] = ogBMP[x][y];

cout << "xr,yr = " << xr << ',' << yr << "...";
if (xr < 0 || xr > 28 || yr < 0 || yr > 28) cout << "BAD!" << endl;
else cout << "good" << endl;
}```

Here is part of the output:

Essentially im trying to make a new matrix out of old one, by placing 1s in rotated positions. Not sure how to fix this problem though.

Hello there.

Last Edited: Sun. Jul 22, 2018 - 02:54 PM

What is "BMPCENTER" and how is that the x and y center coordinates are the same? Shouldn't you have XCENTER and YCENTER to match the formulae I showed in #2 which calls them x0 and y0? Or is it 14,14 ?

(Actually how do you represent 29/2 in integers exactly?)

As I said back in #3 I think you are doing it the wrong way around.

You should step through each element of your destination matrix and calculate where to read from in the source matrix.

You appear to be doing it the other way round by stepping through the source matrix element by element and working out where to write in the destination.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

It was my suggestion to simply draw the graphic and create the bitmap from the graphic.

I enclose an Arduino sketch that draws the arrow and then reads the TFT's GRAM memory to match the pixel colour.

Regarding drawing.

```void draw_arrow(int x, int y, int radius, int flute, uint16_t color, int degree)
{
float angle = degree / 57.29578;
float Cangle = (130) / 57.29578;
int Ax = x + radius * cos(angle - Cangle);
int Ay = y + radius * sin(angle - Cangle);
int Bx = x + radius * cos(angle);
int By = y + radius * sin(angle);
int Cx = x + radius * cos(angle + Cangle);
int Cy = y + radius * sin(angle + Cangle);
int Dx = x + flute * cos(angle + 180 / 57.29578);
int Dy = y + flute * sin(angle + 180 / 57.29578);
tft.fillTriangle(Ax, Ay, Bx, By, Dx, Dy, color);
tft.fillTriangle(Cx, Cy, Bx, By, Dx, Dy, color);
}
```

Yes,  it could be written neater.   But you end up with an array of bitmaps that only take up 4608  bytes of Flash.   Hardly worth storing on SD Card.

And once you have it in Flash memory,  you can manipulate it very easily.

David.

## Attachment(s):

You can do this, but it will take some trig and floating point:

You have an input image, your bitmap(BI), and an output image, your screen(SI).

For each pixel in the output image, use the trig posted above to find the place in the input bitmap. It will almost certainly land between bits, so you average the bits around that point to decide what the bit in the screen image should be.

Perhaps better would be to just store the corners of the arrow, not a bitmap of the arrow. Then to draw it, you use trig to figure the pixels for the 3 corners and use a fill polygon algorithm. That avoids figuring sin and cos for each pixel.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut.

Surely, there must be GUI libraries/toolkits which have all this ready done & dusted ... ?

Top Tips:

1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...

awneil wrote:
Surely, there must be GUI libraries/toolkits which have all this ready done & dusted ... ?
QT and OpenCV instantly spring to mind - but perhaps a little "heavy" for an AVR?

clawson wrote:
QT and OpenCV instantly spring to mind - but perhaps a little "heavy" for an AVR?

Yes.

Thinking more along the lines of:

https://www.segger.com/products/user-interface/emwin/

Of course, one has to bear in mind the capabilities of one's platform.

Or, conversely, pick a platform according to one's required capabilities.

THeres' also a load of "smart" displays - where you offload all this stuff to the display...

Top Tips:

1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...

I posted a program that would generate the bitmaps.   (which included the ready-made result)

Yes,  you can draw graphics from scratch.   Or you can blit a pre-computed bitmap.   Since the image is very small,   it is practical to do either with an AVR on a TFT.

However,   using the XOR approach makes a dramatic difference.    Hey-ho,   there is nothing new.   That is how animated-GIF works.

Of course there are many other ways of doing things.   Not all are suited to TFT hardware.

A common approach is to draw a wire-frame.    Then fill enclosed area with the appropriate colour.   If your hardware suits flood-fill this is a very effective method.

David.