[SDL] Creating new custom surfaces

This topic is 3959 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

I'm trying to resize an SDL surface after loading it with a bitmap. I do this by creating new pixel data by a resize function and then calling SDL_CreateRGBSurfaceFrom() with that pixel data. It works fine for resizing only the height of the image. If the width is resized(irrespective of height), the resulting image shows some weird color pattern mildly resembling the original image. I suspect it has something to do with the pitch, but I didn't find anything wrong there. I would appreciate any help, I'm really at my wit's end after spending two days on this. I wouldn't say no to a lecture on pitch either, I've found very little on the net about that. This is the code, its for 16-bit data only. Thanks, (for reading till here at least)
/* ... */

#define BPP_BMAP 16		    /* bits per pixel */
typedef unsigned short word;	    /* 16 bit integer */

#define align4(X) (((X) + 3) & ~3)  /* return alignment at next multiple of 4 */

#define pixcopy(DEST, DOFF, SRC, SOFF)        (*(word*)((DEST) + (DOFF)) = *(word*)((SRC) + (SOFF)))

/* resizes a bitmap by width or height or both */
void* resizebmap(void **base, int w, int h, float scalew, float scaleh)
{
int x, y, neww, newh;
void *newbase;

neww = (int) (w * scalew);
newh = (int) (h * scaleh);
neww = align4(neww);

/* allocate memory for bitmap data for resized image */
if ((newbase = malloc(neww * newh * BPP_BMAP)) == NULL)
return NULL;

/* resize by expanding each pixel */
for (y = 0; y < newh; y++){
for (x = 0; x < neww; x++){
pixcopy(newbase, y * neww + x, *base, ((int)(y/scaleh))*w +   ((int)(x/scalew)));
}
}
return newbase;
}

main()
{
float xc, yc;
int p;

SDL_Surface *screen, *bmp, *bmp1;
void *pix;

if ((bmp1 = SDL_LoadBMP("test.bmp")) == NULL){
exit(1);
}
if ((bmp = SDL_ConvertSurface(bmp1, screen->format, 0)) == NULL){
exit(1);
}

xc = 1, yc = 1;
pix = resizebmap(&bmp->pixels, bmp->pitch, bmp->h, xc, yc);

p = bmp->pitch * xc;
p = align4(p);

SDL_BlitSurface(bmp1, NULL, screen, NULL);

/* ... */
}


[Edited by - manu1001 on February 6, 2008 8:09:41 AM]

Share on other sites
It would probably just be simpler to use something like SDL_gfx's surface zooming than writing your own.

Share on other sites
The problem here is not with resizing, but in creating SDL surfaces with custom pixel data.
I wish to use this for other image transformations as well.

Share on other sites
You could try creating a new surface and then blitting from the old surface to the new surface...

Share on other sites
Pitch is the physical surface width, whereas width is the logical surface width. Pitch can often be bigger than width, leaving unused space on the side of the image. So if you want to skip down a row, you add on pitch*pixelSize. But for reading within a row, you only want values 0-(pitch-1). So you can't just use them interchangeably, and you can't pass in the image pitch as the width.

Share on other sites

Yes, I am creating a new surface (using SDL_CreateRGBSurfaceFrom()) but the pixel data for it is not in another surface so that i can blit, rather I'm generating that using resizebmap() function.

I somehow need to associate the data returned by resizebmap() with the new surface.

Again, the method used in the code above works for surfaces of same pitch(scanline), which led me to suspect that pitch is the culprit here.

Share on other sites

I've tried as you said and now pass both width and pitch as parameters. It isn't working this way either. In fact there's an additional problem now, it displays only half the image always. No clue why.

Here are the changed parts of the code

Function:

/* resizes a bitmap by width or height or both */void* resizebmap(void **base, int w, int h, int p, float scalew, float scaleh){  int x, y, neww, newh, newp;  void *newbase;  neww = (int) (w * scalew);  newh = (int) (h * scaleh);  newp = (neww * BPP_BMAP);  newp = align4(newp);  /* allocate memory for bitmap data for resized image */  if ((newbase = malloc(newp * newh)) == NULL)      return NULL;  /* resize by expanding each pixel */  for (y = 0; y < newh; y++){      for (x = 0; x < neww; x++){          pixcopy(newbase, y*newp + x, *base, ((int)(y/scaleh))*p + ((int)(x/scalew)));      }  }  return newbase;}

call:

pix = resizebmap(&bmp->pixels, bmp->w, bmp->h, bmp->pitch, xc, yc);

Share on other sites
If you're allocating a lump of memory, you don't need to worry about pitch. Your pitch will always equal width. It's when the API allocates memory and you want to read or amend it that you must concern yourself with the pitch that it chooses, otherwise you can't tell where each row starts.

You seem to be mixing your units here, measuring pitch in bytes and width in pixels. Not recommended, mostly because that will get things wrong by a factor of 2 when you have 2 bytes per pixel, obviously. Remove newp, and change 'y*newp + x' to 'y*neww + x' and see how it goes.

[Edited by - Kylotan on February 8, 2008 4:59:48 AM]

Share on other sites
The Final Solution!

Finally got it (whew). You're right Kylotan, I did get my units mixed up. The pixcopy function copied word elements, but I was sending byte arguments(even that was incorrect actually, I was sending y as byte and x as word). Here's the modified version with pixcopy accessing byte elements. It works perfectly(for now).

There are a few other small "tunings", for the resize function to work properly.

And I would like to give a precise definition of pitch here. Is it fine?

Pitch = number of bytes in a single row of a surface + number of bytes required to make the width of the row word aligned.

/* ... */#define BPP_BMAP 2		    /* bytes per pixel */typedef unsigned short word;	    /* 16 bit integer */#define align4(X) (((X) + 3) & ~3)  /* return alignment at next multiple of 4 */#define pixcopy(DEST, DOFF, SRC, SOFF)        (*((byte*)(DEST) + (DOFF) + 0) = *((byte*)(SRC) + (SOFF) + 0)),  (*((byte*)(DEST) + (DOFF) + 1) = *((byte*)(SRC) + (SOFF) + 1))/* resizes a bitmap by width or height or both */void* resizebmap(void *base, int w, int h, int p, float scalew, float scaleh){  int x, y, neww, newh, newp;  void *newbase;  neww = ((int)(w * scalew)) + 1;  newh = ((int)(h * scaleh)) + 1;  newp = neww * BPP_BMAP;  newp = align4(newp);  /* allocate memory for bitmap data for resized image */  if ((newbase = malloc(newp* newh)) == NULL)      return NULL;  /* resize by expanding each pixel */  for (y = 0; y < newh; y++){      for (x = 0; x < neww; x++){          pixcopy(newbase, y*newp + x*BPP_BMAP, base, ((int)(y/scaleh))*p + ((int)(x/scalew)*BPP_BMAP));      }  }  return newbase;}main(){  float xc, yc;  int p;  SDL_Surface *screen, *bmp, *bmp1;  void *pix;  if ((bmp1 = SDL_LoadBMP("test.bmp")) == NULL){       printf("Couldn't load bitmap\n");       exit(1);  }  if ((bmp = SDL_ConvertSurface(bmp1, screen->format, 0)) == NULL){      printf("Couldn't load bitmap\n");      exit(1);  }  xc = 1.5, yc = 1.5;  pix = resizebmap(bmp->pixels, bmp->w, bmp->h, bmp->pitch, xc, yc);          p = (int)(bmp->w * xc + 1) * bmp->format->BytesPerPixel;  p = align4(p);  bmp1 = SDL_CreateRGBSurfaceFrom (pix, bmp->w * xc + 1, bmp->h * yc + 1, bmp->format->BitsPerPixel, p,                                     bmp->format->Rmask, bmp->format->Gmask, bmp->format->Bmask, bmp->format->Amask);  SDL_BlitSurface(bmp1, NULL, screen, NULL);  /* ... */}

Share on other sites
No, pitch could be any number at all that is equal to or larger than the width of the surface. It won't just be rounded up to the nearest word alignment - it might be rounded up to the nearest 16, or the nearest power of 2, or some other arbitrary maximum. It allows graphics cards to store the memory in the most speed-efficient way, by perhaps wasting a little memory.

1. 1
2. 2
3. 3
Rutin
19
4. 4
khawk
14
5. 5
frob
12

• 9
• 11
• 11
• 23
• 12
• Forum Statistics

• Total Topics
633659
• Total Posts
3013208
×