Register | Login
Views: 19364387
Main | Memberlist | Active users | ACS | Commons | Calendar | Online users
Ranks | FAQ | Color Chart | Photo album | IRC Chat
11-02-05 12:59 PM
0 user currently in Programming. | 3 guests
Acmlm's Board - I2 Archive - Programming - And then there's image conversion | |
Add to favorites | "RSS" Feed | Next newer thread | Next older thread
User Post
HyperLamer
<||bass> and this was the soloution i thought of that was guarinteed to piss off the greatest amount of people

Sesshomaru
Tamaranian

Level: 118

Posts: 7934/8210
EXP: 18171887
For next: 211027

Since: 03-15-04
From: Canada, w00t!
LOL FAD

Since last post: 2 hours
Last activity: 2 hours
Posted on 10-22-05 12:48 PM Link | Quote
Alright, so I wrote a program using SDL and SDL_Image which can load a JPG/GIF/PNG, get the raw pixel data and export it to a 24-bit BMP. It works in most cases but in certain GIFs and PNGs, it seems to miss a byte (though of course SDL's functions can draw it fine). For example, this turns into this, and this (old screenshot alert ) turns into this.

if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("Failed to load SDL library: %s\n",SDL_GetError());
exit(1);
}
printf("Loaded OK\n");
atexit(Unload);

screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE | SDL_ANYFORMAT | SDL_DOUBLEBUF);
if(!screen)
{
printf("Video init failed: %s\n", SDL_GetError());
exit(1);
}
printf("Initialized screen OK\n");
image = IMG_Load("F:\\Images\\57Chevy.gif");

if(image)
{
printf("Loaded image OK\nImage dimensions are %dx%d @ %dbpp (%dBpp)\n",image->w,image->h,image->format->BitsPerPixel,image->format->BytesPerPixel);
}
else
{
printf("Failed to load image: %s\n",SDL_GetError());
exit(1);
}

SDL_FillRect(screen,NULL,0); //Clear the screen
SDL_WM_SetCaption("SDL Image Test","wtf is this string for?");

hWindow = FindWindow(NULL,"SDL Image Test");
printf("Window = %08X\n",hWindow);
hDC = GetDC(hWindow);
printf("DC = %08X\n",hDC);

MainDC = CreateCompatibleDC(NULL);
printf("MainDC = %08X\n",MainDC);

MainBMP = CreateCompatibleBitmap(GetDC(NULL),image->w,image->h);
printf("MainBMP = %08X\n",MainBMP);

OldBMP = SelectObject(MainDC,MainBMP);
printf("OldBMP = %08X\n",OldBMP);


FILE* fp;
fp = fopen("F:\\Images\\test.bmp","wb");

unsigned int ImgSize = image->w * image->h;
int x=0, y=0;

BITMAPFILE b;
b.ID[0] = 'B';
b.ID[1] = 'M';
b.FileSize = sizeof(BITMAPFILE) + (ImgSize * 3);
b.Reserved1 = 0;
b.Reserved2 = 0;
b.ImageDataPtr = sizeof(BITMAPFILE);
b.Header.biSize = sizeof(BITMAPINFOHEADER);
b.Header.biWidth = image->w;
b.Header.biHeight = image->h;
b.Header.biPlanes = 1;
b.Header.biBitCount = 24;
b.Header.biCompression = BI_RGB;
b.Header.biSizeImage = 0;
b.Header.biXPelsPerMeter = 0;
b.Header.biYPelsPerMeter = 0;
b.Header.biClrUsed = 0;
b.Header.biClrImportant = 0;
fwrite(&b,sizeof(b),1,fp);

BYTE BMPData[3];
BYTE* data = image->pixels + (image->format->BytesPerPixel * (image->w * (image->h - 1)));

int i;
for(i=0;i<ImgSize;i++)
{
switch(image->format->BytesPerPixel)
{
case 1:
SDL_GetRGB(*data,image->format,&BMPData[2],&BMPData[1],&BMPData[0]);
break;

case 2:
SDL_GetRGB(*(short*)data,image->format,&BMPData[2],&BMPData[1],&BMPData[0]);
break;

case 3:
case 4: //Ignore the alpha byte
SDL_GetRGB(*(int*)data,image->format,&BMPData[2],&BMPData[1],&BMPData[0]);
break;
}
fwrite(&BMPData[0],1,1,fp);
fwrite(&BMPData[1],1,1,fp);
fwrite(&BMPData[2],1,1,fp);

data += image->format->BytesPerPixel;
x++;
if(x >= image->w)
{
data -= image->format->BytesPerPixel * (image->w * 2);
x = 0;
y++;
}
SetPixel(MainDC,x,y,(BMPData[0] << 16) | (BMPData[1] << 8) | BMPData[2]);

}
fclose(fp);

while(true)
{
SDL_BlitSurface(image, NULL, screen, NULL);
BitBlt(hDC, 0, 32, 32, 32, MainDC, 0, 0, SRCCOPY);
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
printf("Program exiting\n");
exit(0);
break;
}
}
Sleep(50);
}


I'm willing to bet the 16-bit and 32-bit conversions don't work as I haven't tested them yet, but that's not important right now. The first image (the car) is 8-bit and the second is 24-bit. I've tried several other images of various formats and bit depths and they all seem to work.

BTW don't mention the messy code, this is just a test.

[edit] Fixed some < and [ being made into HTML.


(edited by Santa Claus on 10-22-05 03:48 AM)
(edited by Santa Claus on 10-22-05 07:12 PM)
creaothceann

Red Paragoomba
Level: 11

Posts: 44/50
EXP: 5903
For next: 82

Since: 01-27-05

Since last post: 21 hours
Last activity: 21 hours
Posted on 10-24-05 11:25 PM Link | Quote
Seems like you're not reading / writing the correct number of bytes per line. Note that the BMP format (among others) expects some "dummy" bytes if the image width is not a multiple of 8.

See wotsit.org for the details.
HyperLamer
<||bass> and this was the soloution i thought of that was guarinteed to piss off the greatest amount of people

Sesshomaru
Tamaranian

Level: 118

Posts: 7988/8210
EXP: 18171887
For next: 211027

Since: 03-15-04
From: Canada, w00t!
LOL FAD

Since last post: 2 hours
Last activity: 2 hours
Posted on 10-25-05 12:43 AM Link | Quote
Nice site! Apparently there is some padding required. I'll try that... Bah. Still not quite working. I fixed it up to pad each scanline to 32 bits as the document mentioned:

fwrite(&BMPData[0],1,1,fp);
fwrite(&BMPData[1],1,1,fp);
fwrite(&BMPData[2],1,1,fp);

data += image->format->BytesPerPixel;
x++;
if(x >= image->w)
{
data -= image->format->BytesPerPixel * (image->w * 2);
x = 0;
y++;
while(ftell(fp) % 4) fwrite(&zero,1,1,fp); //Zero-pad each scanline
}


The car still comes out skewed (the opposite way ), but blue. Other images which worked before come out miscoloured: this comes out as this (notice the miscoloured disk and red light turned green).
creaothceann

Red Paragoomba
Level: 11

Posts: 46/50
EXP: 5903
For next: 82

Since: 01-27-05

Since last post: 21 hours
Last activity: 21 hours
Posted on 10-25-05 12:24 PM Link | Quote
Some random thoughts about the code... (I'm not programming in C btw.)

- You create a BITMAPFILE b. It's created on the stack, right? If not, then it should be already initialized with zeroes; otherwise I'd just fill it. Either way you could remove the "foo = 0" lines.

- Are you doing things pixel-per-pixel? That'd be really slow. Just allocate an array and copy the lines. Dealing with the padding bytes should then be easier as well.

- Why don't you define "." as "->", as other languages do? Would that break stuff?


(edited by creaothceann on 10-25-05 03:25 AM)
HyperLamer
<||bass> and this was the soloution i thought of that was guarinteed to piss off the greatest amount of people

Sesshomaru
Tamaranian

Level: 118

Posts: 7997/8210
EXP: 18171887
For next: 211027

Since: 03-15-04
From: Canada, w00t!
LOL FAD

Since last post: 2 hours
Last activity: 2 hours
Posted on 10-25-05 11:46 PM Link | Quote
Yeah, I intend to replace the x=0 with memfill() once it's working properly. The SetPixel() is only there to see if it's rendering right, it's actually pretty fast. And . is different from ->; in my experience, x.blah is used when x is a struct, and x->blah is used when x is a pointer to a struct. That's how I've been using them, anyway, and it's worked so far.
Disch

Micro-Goomba
Level: 4

Posts: 7/16
EXP: 226
For next: 53

Since: 10-21-05

Since last post: 12 hours
Last activity: 1 day
Posted on 10-30-05 08:38 PM Link | Quote
Originally posted by creaothceann

- You create a BITMAPFILE b. It's created on the stack, right? If not, then it should be already initialized with zeroes;


Neither heap nor stack memory is ever initialized with 0. Uninitialized variables ALWAYS have undefined contents in C/C++. Never assume the state of anything -- always write before you read.

But yeah... I would agree that HH should do a memset(&b,0,sizeof(BITMAPFILE)); before filling his struct. Not only does it avoid having to set several vars to zero, but it also makes sure you don't MISS setting any vars to zero.


- Why don't you define "." as "->", as other languages do? Would that break stuff?


As HH pointed out, dot (.) is different from the arrow (->) in that the arrow performs indirection.


foo->bar

is exactly the same as:

(*foo).bar

and:

foo[0].bar

but is different from:

foo.bar


As for your most recent problem, HH -- the FILE doesn't need to be padded to 32-bits... but the BITS do. A scanline may end on an odd offset in the file... just so long as the offset from the start of the bit data lands on the 4-byte boundary.

The pitch of the bit data can be determined with the following formula (for windows bitmaps, at least):


pitch = (((width * bits_per_pixel) + 31) / 32) * 4;


So at the end of the scanline, after outputting N bytes of pixel data -- merely output (pitch - N) bytes of padding. ftell(), your position in the file is irrelevent.


(edited by Disch on 10-30-05 11:40 AM)
HyperLamer
<||bass> and this was the soloution i thought of that was guarinteed to piss off the greatest amount of people

Sesshomaru
Tamaranian

Level: 118

Posts: 8139/8210
EXP: 18171887
For next: 211027

Since: 03-15-04
From: Canada, w00t!
LOL FAD

Since last post: 2 hours
Last activity: 2 hours
Posted on 10-30-05 10:48 PM Link | Quote
So something like this?
int i, j, pitch;
pitch = (((image->w * 24) + 31) / 32) * 4;
for(i=0;i<ImgSize;i++)
{
switch(image->format->BytesPerPixel)
{
case 1:
SDL_GetRGB(*data,image->format,&BMPData[2],&BMPData[1],&BMPData[0]);
break;

case 2:
SDL_GetRGB(*(short*)data,image->format,&BMPData[2],&BMPData[1],&BMPData[0]);
break;

case 3:
case 4: //Ignore the alpha byte
SDL_GetRGB(*(int*)data,image->format,&BMPData[2],&BMPData[1],&BMPData[0]);
break;
}
fwrite(&BMPData[0],1,1,fp);
fwrite(&BMPData[1],1,1,fp);
fwrite(&BMPData[2],1,1,fp);

data += image->format->BytesPerPixel;
x++;
if(x >= image->w)
{
data -= image->format->BytesPerPixel * (image->w * 2);
for(j=0;j<(pitch - (x * 3));j++)
fwrite(&zero,1,1,fp); //Zero-pad each scanline
x = 0;
y++;
}
SetPixel(MainDC,x,y,(BMPData[0] << 16) | (BMPData[1] << 8) | BMPData[2]);
}

That fixes the colours, but things are still getting skewed.


(edited by HyperHacker on 10-30-05 01:50 PM)
Disch

Micro-Goomba
Level: 4

Posts: 9/16
EXP: 226
For next: 53

Since: 10-21-05

Since last post: 12 hours
Last activity: 1 day
Posted on 10-30-05 11:01 PM Link | Quote
Originally posted by HyperHacker
data -= image->format->BytesPerPixel * (image->w * 2);


use the pitch, my son! The pitch!

The width should never be used in this manner -- that's what the pitch is for.

data -= image->pitch * 2;

or whatever the SDL pitch var name is (possibly 'p'?)


(edited by Disch on 10-30-05 02:02 PM)
HyperLamer
<||bass> and this was the soloution i thought of that was guarinteed to piss off the greatest amount of people

Sesshomaru
Tamaranian

Level: 118

Posts: 8156/8210
EXP: 18171887
For next: 211027

Since: 03-15-04
From: Canada, w00t!
LOL FAD

Since last post: 2 hours
Last activity: 2 hours
Posted on 10-31-05 07:16 AM Link | Quote
Well that's interesting, but segfaults aren't really what I'm after here.
Disch

Micro-Goomba
Level: 4

Posts: 13/16
EXP: 226
For next: 53

Since: 10-21-05

Since last post: 12 hours
Last activity: 1 day
Posted on 10-31-05 07:57 AM Link | Quote
Surface pitch is the number of bytes between pixel rows on a surface. So if an 8-bit image has a width of 256 and a pitch of 256 -- then there's no padding. If the pitch is 260 then there's 4 bytes of padding. (Though "padding" is a misleading word... as this space may be used by other surfaces or for other video memory and should not be zeroed/touched by your program!)

Nearly all surfaces in hardware memory (and even most/many in software memory) will have some padding -- ie the pitch will NOT match the width... and you much never use the width when calculating the pitch... the two are unrelated. An image with a width of 2 pixels might have a pitch of 512 bytes... you never know (though that paticular example is pretty unlikely).

SDL will fill the 'pitch' member of the SDL_Surface struct with an appropriate value, provided the SDL_Surface is a real surface (so unless you tried to make one without SDL_CreateSurface or something, you should be fine). You also may have to LOCK the surface for the pitch to be valid (though I'm not 100% sure with SDL) -- but you really should already have it locked if you're accessing the pixel data. If you're reading pixels from an unlocked surface, shame on you ;P

The only things I can think of, is either your pitch value is bad somehow (not likely), or you're starting at the wrong point and coming out before the start of the buffer (very likely). I see that you're starting from the bottom of the image and working up... when you calculate to find the last scanline of the image... are you still using the width? I'm willing to bet you are ;D

If you want to retrieve the last scanline of the image, use the following formula to get the offset (in bytes) from the start of the buffer:

offset = (image_height - 1) * image_pitch;

The width should NOT be used in any calculation of this sort... ever.

The pitch... my son. The pitch.

EDIT (again)

I actually just realized that the formula I gave you was wrong. Whoops!

what I gave you before:
data -= image->pitch * 2;

that's assumed you're past the padding! Silly me!

what you really want is:

data -= image->pitch + (image->w * image->format->BytesPerPixel);

Note I only use width here to subtract the bytes you wrote to the buffer -- so my previous statement about never using the width in these kinds of calculations stands... the subtracted width just puts you back to the start of the scanline... and you subtract the pitch to get to the previous scanline.

My mistake. Sorry about that. the other assumptions I made pre-edit may be mistaken, however the stuff about the pitch is still a good read so I'll leave it.


(edited by Disch on 10-30-05 10:58 PM)
(edited by Disch on 10-30-05 11:00 PM)
(edited by Disch on 10-30-05 11:06 PM)
creaothceann

Red Paragoomba
Level: 11

Posts: 49/50
EXP: 5903
For next: 82

Since: 01-27-05

Since last post: 21 hours
Last activity: 21 hours
Posted on 11-01-05 02:44 PM Link | Quote
Originally posted by Disch
Neither heap nor stack memory is ever initialized with 0. Uninitialized variables ALWAYS have undefined contents in C/C++.

I was thinking about variables that are included in the executable file. Sort of like this:


program Test;

var tmp : Integer;

begin
WriteLn(tmp);
ReadLn;
end.


Output is "0".

[/offtopic]


(edited by creaothceann on 11-01-05 05:44 AM)
Disch

Micro-Goomba
Level: 4

Posts: 15/16
EXP: 226
For next: 53

Since: 10-21-05

Since last post: 12 hours
Last activity: 1 day
Posted on 11-01-05 09:06 PM Link | Quote
Regardless or whether or not you get 0 from that, you should never assume you get zero from that because you didn't specify. In C/C++, uninitialized variables are undefined. They could contain anything.

If you really need it to be defined from the get-go. A simple "int var = 0;" will do.
Add to favorites | "RSS" Feed | Next newer thread | Next older thread
Acmlm's Board - I2 Archive - Programming - And then there's image conversion | |


ABII


AcmlmBoard vl.ol (11-01-05)
© 2000-2005 Acmlm, Emuz, et al



Page rendered in 0.016 seconds.