Page 1 of 2

[SOLVED][SDL] Rotating a specific area of a surface

Posted: Wed Mar 24, 2010 1:44 pm
by xiphirx

Code: Select all

#include <SDL/SDL.h>
#include <math.h>
#include <unistd.h>
#include <iostream>
using namespace std;

void SDL_Rotate(SDL_Surface *surface, SDL_Rect *rect, float angle, int x, int y) {
   SDL_LockSurface(surface);
   int bpp = surface->format->BytesPerPixel;
   int white = SDL_MapRGB(surface->format,0xff,0xff,0xff);
   char* pixels = (char*)malloc(surface->w*surface->h*bpp);
   for (int i = surface->w*surface->h; i >= 0; --i) {
      char* c = pixels+i*bpp;
      memcpy(c,&white,bpp);
   }
   for(int sx = surface->w; sx >= 0; --sx) {
      for (int sy = surface->h; sy >= 0; --sy) {
         if (sx >= rect->x && sx < (rect->x+rect->w) && sy >= rect->y && sy < (rect->y+rect->h)) { //Point in surface is part of rect
            float curangle;
            bool xshift = (abs(sx) > abs(x))?1:0;
            bool yshift = (abs(sy) > abs(y))?0:1;
            if (sx-x == 0) {curangle = (abs(sy) > abs(y))?270:90;}
            else {curangle = atan((float)(sy-y+yshift)/(float)(sx-x+xshift)) * 180/3.14159;}
            if (abs(sx-x) != sx-x) { //Correct angle, since atan() only returns [-pi/2,pi/2]
               curangle=180.0-curangle;
            }
            if (fabs(curangle) != curangle) {curangle+=360;}
            float l = sqrt(pow(sx-x,2)+pow(sy-y,2));
            char* p = (char*)surface->pixels+sx*bpp+sy*surface->pitch;
            char* q = (char*)pixels+(int)((cos((angle+curangle)*3.14159/180)*l)+x)*bpp+(int)((sin((angle+curangle)*3.14159/180)*l)+y)*surface->pitch;
            memcpy(q,p,bpp);
            if(memcmp(q+bpp,&white,bpp) == 0) {
               memcpy(q+bpp,q,bpp); //To effectively cover without many/any holes
            }
         }
      }
   }
   memcpy(surface->pixels,pixels,surface->w*surface->h*bpp);
   SDL_UnlockSurface(surface);
}

int main() {
   SDL_Init(SDL_INIT_VIDEO);
   SDL_Surface *videoSurface = SDL_SetVideoMode(640,480,16,0);
   int white = SDL_MapRGB(videoSurface->format,0xff,0xff,0xff);
   int black = SDL_MapRGB(videoSurface->format,0x00,0x00,0x00);
   SDL_FillRect(videoSurface,NULL,white);
   SDL_Rect* rect = new SDL_Rect();
   rect->h = 120;
   rect->w = 100;
   rect->x = videoSurface->w/2-60;
   rect->y = videoSurface->h/2-50;
   SDL_FillRect(videoSurface,rect,black);
   SDL_LockSurface(videoSurface);
   char* original_pixels = (char*)malloc(videoSurface->w*videoSurface->h*videoSurface->format->BytesPerPixel);
   memcpy(original_pixels,videoSurface->pixels,videoSurface->w*videoSurface->h*videoSurface->format->BytesPerPixel);
   SDL_UnlockSurface(videoSurface);
   SDL_UpdateRect(videoSurface,0,0,videoSurface->w,videoSurface->h);
   atexit(SDL_Quit);
   sleep(1);
   
   for(int t = 15; t < 360; t+=15) {
      SDL_Rotate(videoSurface,rect,t,rect->x+rect->w/2,rect->y+rect->h/2);
      SDL_UpdateRect(videoSurface,0,0,videoSurface->w,videoSurface->h);
      sleep(1);
      SDL_LockSurface(videoSurface);
      memcpy(videoSurface->pixels,original_pixels,videoSurface->h*videoSurface->w*videoSurface->format->BytesPerPixel);
      SDL_UnlockSurface(videoSurface);
   }
} 
http://forums.libsdl.org/viewtopic.php? ... 88aa59f2b8

I have found that function that *should* serve my needs, but I was wondering if anyone else had a function they were willing to lend? or could help me fix the above function so it works properly? I am building a tile map editor, and the user should be able to rotate specific tiles, and add them into an overall tile surface... I haven't done any surface merging yet D:

Re: [SDL] Rotating a specific area of a surface

Posted: Wed Mar 24, 2010 9:19 pm
by qpHalcy0n
Having a hard time understanding what EXACTLY you're looking for here. I have an idea, but if you rotate tiles and then stamp them back down you're gonna have some really nasty holes there.

Either way, that function at a glance looks to be extraordinarily inefficient.

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 1:16 am
by xiphirx
What I mean is that say you have a sprite sheet, and you clip it into rects (which is my case). I want to be able to rotate a single frame of the sprite from the whole sheet, and then merge, or add it onto the end of the sprite sheet.
So if my sprite sheet was
| / -- \ |

and say I wanted to rotate the second frame 90 degrees

| / | \ |

that would be the result.

and if I wanted it to be added,

| / -- \ | |

This probably requires a complex function that I am probably not going to find help for right?

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 12:01 pm
by Falco Girgis
qpHalcy0n wrote:Having a hard time understanding what EXACTLY you're looking for here. I have an idea, but if you rotate tiles and then stamp them back down you're gonna have some really nasty holes there.

Either way, that function at a glance looks to be extraordinarily inefficient.
Yeah, haha. I can't pretend like I could write an efficient software-rendered method of rotating a texture, but this really does make me appreciate hardware acceleration in 2D.

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 12:03 pm
by xiphirx
Judging from your post ...
xiphirx wrote: This probably requires a complex function that I am probably not going to find help for right?

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 12:09 pm
by hurstshifter
Xiphirx, when you say rotate the tile you mean at 90 degree intervals, correct? I think some might be confused into thinking you want to be able to freely rotate and place the tile at any angle. Although this might be what you're looking for, I'm not sure.

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 12:10 pm
by qpHalcy0n
I think your clarification of what you're looking for just made me more confused :]
This may look fine for a 90 degree rotation, but if you rotate a tile 13.3 degrees, then it's going to look like hell (void space and overlap)

This should be a trivial problem if we just know EXACTLY what you're looking for.

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 12:23 pm
by xiphirx
Hopefully this helps *clever file name eh?*

Image

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 1:01 pm
by qpHalcy0n
Mkay, then you've got a few design considerations to take into.....consideration... :\

If you want to add the rotated tile BACK into the original sprite sheet, you're looking at reallocating the space for it and offsetting every subsequent tile in the new sheet. The other option is to just put it back where it was.

The other design consideration to make is one that I think is more important. Why do we need to save the rotated tiles as an asset on disk?? The quick answer is....you don't. There is no actual exchange of data here whatsoever. In other words the only thing that's changed is the orientation of the tile (which happens to be on quadrantal angles). The color has not changed, the bit depth has not changed, nothing at all has REALLY changed.

It's not that it can't be done, but the original method is very slow. The reason it's going to be very slow is the liberal usage of the surface locks. No matter what API it is (SDL, DirectX, OpenGL), when you lock a surface it enters a critical section...halts all processes....and literally maps memory to the caller (wherever that memory may be). If that memory is actually resident on the video board, then you're looking at a possible bandwidth issue on top of all of that especially if you're on an older architecture (like AGP or PCI). So try to avoid locking those surfaces if you can at all.

The other issue there is that the math is extremely roundabout. atan2 (arc tangent of y/x as opposed to x) will work just fine but you can pre-calculate sin/cos for the 90, 180, 270 angles and arrive at a very fast rotation.

There's alot of copying and clearing (inefficient clearing) of surfaces that does not need to happen. That's just a bad algorithm. So if you want to stick w/ keeping the changes on disk we can help.

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 1:10 pm
by hurstshifter
Hate to say it, but why don't you just include all rotation possibilities in the spritesheet itself?

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 1:22 pm
by xiphirx
qpHalcy0n wrote:Mkay, then you've got a few design considerations to take into.....consideration... :\

If you want to add the rotated tile BACK into the original sprite sheet, you're looking at reallocating the space for it and offsetting every subsequent tile in the new sheet. The other option is to just put it back where it was.

The other design consideration to make is one that I think is more important. Why do we need to save the rotated tiles as an asset on disk?? The quick answer is....you don't. There is no actual exchange of data here whatsoever. In other words the only thing that's changed is the orientation of the tile (which happens to be on quadrantal angles). The color has not changed, the bit depth has not changed, nothing at all has REALLY changed.

It's not that it can't be done, but the original method is very slow. The reason it's going to be very slow is the liberal usage of the surface locks. No matter what API it is (SDL, DirectX, OpenGL), when you lock a surface it enters a critical section...halts all processes....and literally maps memory to the caller (wherever that memory may be). If that memory is actually resident on the video board, then you're looking at a possible bandwidth issue on top of all of that especially if you're on an older architecture (like AGP or PCI). So try to avoid locking those surfaces if you can at all.

The other issue there is that the math is extremely roundabout. atan2 (arc tangent of y/x as opposed to x) will work just fine but you can pre-calculate sin/cos for the 90, 180, 270 angles and arrive at a very fast rotation.

There's alot of copying and clearing (inefficient clearing) of surfaces that does not need to happen. That's just a bad algorithm. So if you want to stick w/ keeping the changes on disk we can help.
I never said that the above function was my final function lol, it was just one that I found that supposedly fits my needs..
So if you want to stick w/ keeping the changes on disk we can help.
What do you mean?
Hate to say it, but why don't you just include all rotation possibilities in the spritesheet itself?
Er, well, this is a tile map editor, I want the user to be able to import tiles, and rotate them if they need to (without going back to their image editor [convenience]). I am also going to include a way to rotate the whole map to a different orientation (half of this is done, I just need to rotate the tiles). So including all the possibilities is tedious, and an annoyance that I am trying to improve on.

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 1:51 pm
by qpHalcy0n
The first step is to not take offense. I'm simply telling you how that function probably will not work. That is all....no more, no less.
If you're going to solve your problem you need to take a constructive attitude towards it and perhaps see it from a different perspective.

What we're SAYING is that rotating a tile isn't really a CHANGE. If you want to rotate a tile in a tile sheet, hell you can do that in code.....easy peasy (you don't need to save it to disk). It's a pre-render step. It's not an image space step. I wouldn't consider a rotation an "edit". If you were to change the pixel format/bit depth, the width, the height, or things like that, then yes....that would be an imaging change which would result in destroying the image and rebuilding it. These are the things you would want to save to disk.

So I'm not understanding why a tile rotation would need to be saved. If you want to rotate a tile, you'd rotate the rectangle that SAMPLES the tilesheet when rendering.

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 1:53 pm
by lotios611
Why don't you use OpenGL? I think it would be way easier than trying to do it in SDL.

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 1:59 pm
by xiphirx
qpHalcy0n wrote:The first step is to not take offense. I'm simply telling you how that function probably will not work. That is all....no more, no less.
If you're going to solve your problem you need to take a constructive attitude towards it and perhaps see it from a different perspective.

What we're SAYING is that rotating a tile isn't really a CHANGE. If you want to rotate a tile in a tile sheet, hell you can do that in code.....easy peasy (you don't need to save it to disk). It's a pre-render step. It's not an image space step. I wouldn't consider a rotation an "edit". If you were to change the pixel format/bit depth, the width, the height, or things like that, then yes....that would be an imaging change which would result in destroying the image and rebuilding it. These are the things you would want to save to disk.

So I'm not understanding why a tile rotation would need to be saved. If you want to rotate a tile, you'd rotate the rectangle that SAMPLES the tilesheet when rendering.
I wasn't taking offense, but whatever. Anyway I see what you're saying, but it has to be saved, because the game that loads the map has no way of know if the tile is supposed to be rotated pre-rendering (I did not make the game, so I cannot change that). So, yes, I would like to save it to the disk.

What do you mean by rotate the rectangle that samples the tile sheet? can I do that? :/
Why don't you use OpenGL? I think it would be way easier than trying to do it in SDL.
No knowledge of OpenGL, and it looks too complicated for me.

Re: [SDL] Rotating a specific area of a surface

Posted: Thu Mar 25, 2010 2:10 pm
by qpHalcy0n
What I'm trying to do is get you to really think the problem though. I'm not a huge fan of simply posting a function and saying "Here ya go....this works". That plug and play approach doesn't get anyone learning anything....."but whatever".

I think if you'll look at some of the design considerations I mentioned earlier you'll see that there are some issues you'll be confronted with that you'll have to get figured out.

But I'll point you in a much easier direction. (Which only requires one lock)
At 90 degree angles the image will do one of three things.

1) You'll swap the rows w/ the columns
2) You'll invert the rows
3) You'll invert the columns