Skip to main content
GameDev.net gamedev.net
🔒 Locked

extracting sprites.

Started by Dospro Feb 26, 2006 at 2:44 PM 15 replies 2.9k views
Original Post
Dospro
Dospro
Hi. I have a little but complex doubt. I have a bitmap, which contains a lot sprites in a black background. I need an algorithm which can extract them to different bitmaps each sprite. I mean, cut the sprite from the main bmp, and create a new bmp with this sprite. Any ideas, about how can i do this?
Ravuya
Ravuya
This is probably more a question for whatever graphics API or tool you are using. I know how to do it in SDL.
Dospro
Dospro
WEll, actually im using SDL. But i dont want to use SDL_image, or something like that.

Just pure SDL.
So, how is it done?
Ravuya
Ravuya
Create a new surface, use a rectangle to blit from the source image to that new surface, and use SDL_SaveBMP to save that new surface to a bitmap.
Dospro
Dospro
¬¬
That is not the problem.
I know how to do that. But i dont know where th rectangle is.
I mean, i nead to serach for the sprite, but i want to do this automatically instead of manual, as you are saying.
Derakon
Derakon
Use a lasso tool in a paint program. Or write one yourself. They aren't too hard to do. Of course, you'll need pixel-level access to the images, which is something that the SDL really doesn't encourage.

It's possible that ImageMagick has this functionality (IM is a suite of batch image manipulation programs), but I haven't checked.
Jetblade: an open-source 2D platforming game in the style of Metroid and Castlevania, with procedurally-generated levels
rip-off
rip-off
Quote:
Original post by Dospro
¬¬
That is not the problem.
I know how to do that. But i dont know where th rectangle is.
I mean, i nead to serach for the sprite, but i want to do this automatically instead of manual, as you are saying.


Imagine you have an array of sprites in a bitmap file. Each of the sprites is exactly the same size (shared widths and heights).

So you want something like so (entirely from memory, test it!):
vector<SDL_Surface*> GetSpritesFromSpriteSheet( SDL_Surface *sheet, int spriteWidth, int spriteHeight, int numAcross ){   // make sure the bitmap is of the right size   if( sheet->w % spriteWidth != 0 || sheet->h % spriteHeight != 0 )   {      // exit the program, throw an exception, whatever      return vector<SDL_Surface*>();   }    int numSpritesAcross = sheet->w / spriteWidth;   int numSpritesDown = sheet->h / spriteheight;   vector<SDL_Surface*> sprites;   for( int x = 0 ; x < numSpritesAcross ; ++x )   {       for( int y = 0 ; y < numSpritesDown ; ++y )       {            SDL_Surface *surface = SDL_CreateRGBSurface( SDL_SWSURFACE,                           spriteWidth,                             spriteHeight,                              32,                            #if SDL_BYTEORDER  == SDL_LIL_ENDIAN                            0x000000ff,                            0x0000ff00,                            0x00ff0000,                            0xff000000                            #else                            0xff000000,                            0x00ff0000,                            0x0000ff00,                            0x000000ff                            #endif            );            // check if surface is good            SDL_Rect source;            source.x = x * spriteWidth;            source.y = y * spriteHeight;            source.w = spriteWidth;            source.h = spriteHeight;            SDL_BlitSurface( sheet, &source, surface, NULL );            sprites.push_back( surface );       }   }   return retVal;}
Kest
Kest
I think he's saying each cell in the larger bitmap can be different sizes.

The best method I've come up with is to draw a background around each sprite. For example, on your large black image, each sprite rectangle would be filled in with a special reserved purple color. Then you simply search for non-black colors, find their area, then blit copy them. Finally, you'll have to replace the purple color with black again in the smaller images. Or just set both of these colors as transparent.

If your sprites can't be disconnected (like a sprite guy dropping a gun), then you can still find the area of each sprite without using the manually drawn purple rectangle. But you'll need heavy access to the bitmap pixels either way.

Let me know if you want more help. I have an old tool I wrote in MFC that does exactly this with a *.bmp file.
Dospro
Dospro
Jjeje. Thats the problem.
The sprites are not the same size neither in multimple positions, they are positioned in random coord. and random size.

But if there is a paint program which can do that for me, it would be perfect.
Kest
Kest
Here's an example of how I set mine up:

Image Hosted by ImageShack.us

Note how each sprite is a different size, but is surrounded by that redish color. I used that color to find the area. It's pretty simple using a layout like this. You scan the whole bitmap for non-redish pixels. Each time you find a one, you scan in all major directions (up, left, right, down) to find the next redish color, giving you the coordinates of the sprite. To simplify, you might want to color that area to the redish (or whatever) color on the large bitmap after each copy. That will prevent you from finding the same cell twice. But you can keep scanning from the same location. You don't have to start over.
Dospro
Dospro
Yea, thts exactly what i need.!!
Can you tell me the lagotithm you used?
Or at least explain me how you did it?
-justin-
-justin-
wow... that looks crazy; usually all mine are the same size and i just cycle through them, or if they are different sizes i manually define them -_-'

i've always been wondering how you do something like that kest!

i'm using directX, but still it'll be fairly similar to SDL

plz explain ^_^
Kest
Kest
I can't help much with the SDL. In fact, I have no idea how SDL images are set up or what levels of access you have.

What I explained should pretty much accomplish it. You'll need individual pixel access to the images. While using DirectX, I just loaded the data in with a system memory surface, locked the surface, and scanned that.

If you've ever scanned bitmap pixels before, here's a rough idea of what the code would look like. Well, assuming the top-left pixel is the first pixel, and that the data continues from left to right then top to bottom. It also assumes there is no padding on the right side of the bitmap (as there probably is using DirectX or Windows bitmaps). There should be a lot of places to find this information if you need it.
// Assuming ImageWidth and ImageHeight are the large image sizes in this// They need passed to each function, but I coded like they were global.void ExtractAllCells(){  PIXEL *ptr = GetFirstPixel();  for(int y=0; y<ImageHeight; y++)  {    for(int x=0; x<ImageWidth; x++)    {      if( *ptr != REDISHCOLOR )      {        // Note that (x,y), in this case, should always be the top-left        // of the cell. But I didn't assume that in the ExtractCell function.        ExtractCell( x, y, ptr );      }      ptr++;    }  }}void ExtractCell(int x, int y, PIXEL *ptr){  // First left pixel  int left;  for(left=x; left>=1; left--)    if( *( ptr + -left + -1 ) == REDISHCOLOR )      break;  // First top pixel  int top;  for(top=y; top>=1; top--)    if( *( ptr - ( ( top + 1 ) * ImageWidth ) ) == REDISHCOLOR )      break;  // Right border (the pixel after the last valid pixel)  int right;  for(right=x+1; right<ImageWidth; right++)    if( *( ptr + right ) == REDISHCOLOR )      break;  // Bottom border (the pixel after the last valid)  int bottom;  for(bottom=y+1; bottom<ImageHeight; bottom++)    if( *( ptr + ( bottom * ImageWidth ) ) == REDISHCOLOR )      break;  // The cell size  int cell_width = right - left;  int cell_height = bottom - top;  // Copy the cell (to a list of smaller images or something)  CopyCell( left, top, cell_width, cell_height );  // Fill the cell with the redish color in the large image  FillArea( left, top, cell_width, cell_height, REDISHCOLOR );}

Let me know if something doesn't look right.

I actually wouldn't recommend using this strategy at all if you can avoid it. It became a huge pain for me when a cell size needed to change. For example, imagine having to add 3 pixels to the width of one of those left most cells in my example picture. A better approach might be to draw each cell in a specific grid system like normal, then crop the blank borders off internally. That way they can change sizes up to the grid size without any hassle.

Anyway, I guess I suck pretty bad at writing pseudo code, but hopefully it least somewhat clarifies the approach I was suggesting.
Dospro
Dospro
Ok. i will have to put a visual example for you to understand me. Lets see, how can i put an image in this post mmm. A yes, its img src.... lets upload the image....
Ok. While searching for examples, i found this which is a perfect sample to show what i need to do. I hope that the owner of the image doesnt get angry for using his/her image without permission.


Thats it. here is the sample image.

Well, what i need is to get each one of the sprites out to a separate bmp, like this:



I want a lot of bmps, containing each one a sprite. And i want this cut process to be done automatically.

Im using SDl as i said, and i have full pixel level acces. I can manipulate(read, write etc)pixels at my will.

Thats why im trying to figure a good algorithm to get the minimal recatangle of each sprite,and take it out.

SO, any ideas?
Derakon
Derakon
Scan through the main image until you find a pixel that is not background. Add that pixel to a queue. Pop the top of the queue, add it to a list, and check all of its neighbors to see if they are not background. Add them to the queue if they are not background and not in the list or queue already. Repeat until the queue is empty. Go through the list and find the extremal points (upper-left and lower-right); extract all pixels between them to a BMP image. Draw background to all the pixels in the list. Repeat.

This is mildly inefficient, but since you only run it once, that's not such a big deal.
Jetblade: an open-source 2D platforming game in the style of Metroid and Castlevania, with procedurally-generated levels
Dospro
Dospro
I dont care about efficiency.
Derakon, can you give me a more detailed explanation about your idea, please? i didn t understand it very well, sorry, my languaje is not english.

Derakon
Derakon
for (i = 0; i < imageWidth; ++i) {  for (j = 0; j < imageHeight; ++j) {    if (!isBackgroundColor(pixel[j]) {      queue.enqueue(Point2D(i, j));      while (!queue.isEmpty()) {        Point2D newPoint = queue.pop();        list.push_back(newPoint);        for (k = newPoint.x - 1; k < newPoint.x + 1; ++k) {          for (l = newPoint.y - 1; l < newPoint.l + 1; ++l) {            Point2D temp = Point2D(k, l);            if (!list.contains(temp) and !queue.contains(temp))              enqueue(temp);          }        }      }    }  }}
By the end of that "while (!queue.isEmpty())" loop, list will contain a list of all the pixels in the frame. So you write those pixels to your BMP file, set all of their colors to the background color (to ensure that you don't grab the same frame twice), and then start over.
Jetblade: an open-source 2D platforming game in the style of Metroid and Castlevania, with procedurally-generated levels

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.