PDA

View Full Version : SDL and OpenGL textures



vgo
14-04-2006, 08:16 AM
I've tried to get my SDL/OpenGL app to work with Lazarus but I've ran into problems.

I can't seem to be able to use any classes like TBitmap etc. My texture loading routines use these and they work just fine in Delphi whether I have a pure SDL application or normal app with forms.

Any workarounds for this or do I have to write my own texture loading routines without the help of TBitmap class for all image formats (BMP, TGA, JPEG, PCX etc.) that I use?

vgo
14-04-2006, 11:55 AM
Got tired of looking for the solution, so I decided the to write the loades myself. www.wotsit.org is the programmer's best friend. :)

If anyone's interested, here's the code to load 8, 24 and 32 bit BMP files without compression and 8bit PCX files with RLE compression.
The loading routines are quite fast, but you're welcome to try to optimize it if you like. :)


type
TTextureData = array [0..0] of Byte;
PTextureData = ^TTextureData;
PTexture = ^TTexture;
TTexture = record
Filename: String;
Width, Height, Size: Integer;
GL_ID: TInt;
GL_Type: Integer;
GL_Parameters: Integer;
GL_Uploaded: Boolean;
end;

function Load_BMP(FileName: String; var TextureData: PTextureData; var Texture: PTexture): Boolean;

const
// Header types
BMP_BM = 'BM'; // Windows 3.1x, 95, NT etc.
BMP_BA = 'BA'; // OS/2 Bitmap Array
BMP_CI = 'CI'; // OS/2 Color Icon
BMP_CP = 'CP'; // OS/2 Color Pointer
BMP_IC = 'IC'; // OS/2 Icon
BMP_PT = 'PT'; // OS/2 Pointer

// Compression types
BMP_BI_RGB = 0; // RGB
BMP_BI_RLE8 = 1; // RLE8
BMP_BI_RLE4 = 2; // RLE4
BMP_BI_BITFIELDS = 4; // Bitfields

// Color types
BMP_COLOR_1 = 1; // Monochrome
BMP_COLOR_4 = 4; // 16 color bitmap
BMP_COLOR_8 = 8; // 256 color bitmap
BMP_COLOR_16 = 16; // 16bit (hight color) bitmap
BMP_COLOR_24 = 24; // 24bit (true color) bitmap
BMP_COLOR_32 = 32; // 32bit (true color) bitmap

BMP_COMPRESSION_NONE = 0; // None (Also identified by BI_RGB)
BMP_COMPRESSION_RLE8 = 1; // RLE 8-bit / pixel (Also identified by BI_RLE4)
BMP_COMPRESSION_RLE4 = 2; // RLE 4-bit / pixel (Also identified by BI_RLE8)
BMP_COMPRESSION_BITFIELDS = 3; // Bitfields (Also identified by BI_BITFIELDS)
type
TBMP_FileHeader = packed record
ID: array [0..1] of Char;
FileSize: LongWord;
Reserved1: Word;
Reserved2: Word;
BitmapDataOffset: LongWord;
end;

TBMP_InfoHeader = packed record
BitmapHeaderSize: LongWord;
Width: LongWord;
Height: LongWord;
Planes: Word;
BitsPerPixel: Word;
Compression: LongWord;
BitmapDataSize: LongWord;
HResolution: LongWord;
VResolution: LongWord;
Colors: LongWord;
ImportantColors: LongWord;
Palette: array [0..255, 0..3] of Byte;
end;

TBMP_Buffer24 = array [0..2] of Byte;
TBMP_Buffer32 = array [0..3] of Byte;

var
BMP_File: TMemoryStream;
BMP_FileHeader: TBMP_FileHeader;
BMP_InfoHeader: TBMP_InfoHeader;
BMP_Buffer: array [0..3] of Byte;
x: LongWord;
y: LongWord;
Offset: LongWord;

b: Byte;

begin
BMP_File := TMemoryStream.Create;
BMP_File.LoadFromFile(FileName);
try
BMP_File.Read(BMP_FileHeader, SizeOf(TBMP_FileHeader));
if (BMP_FileHeader.ID <> BMP_BM) then
raise EUnsupportedFileFormatError.Create('Unsupported BMP file format ' + BMP_FileHeader.ID + '!');
BMP_File.Read(BMP_InfoHeader, SizeOf(TBMP_InfoHeader));
Texture^.Width := BMP_InfoHeader.Width;
Texture^.Height := BMP_InfoHeader.Height;
Texture^.Size := BMP_InfoHeader.Width * BMP_InfoHeader.Height * 3;
Texture^.GL_Type := GL_RGB;
GetMem(TextureData, Texture^.Size);
y := BMP_InfoHeader.Height - 1;
x := 0;
if BMP_InfoHeader.BitsPerPixel = BMP_COLOR_8 then
begin
BMP_File.Seek(BMP_FileHeader.BitmapDataOffset, soFromBeginning);
while BMP_File.Position < BMP_File.Size do
begin
BMP_File.Read(b, 1);
Offset := ((y * BMP_InfoHeader.Width) + x) * 3;
TextureData^[Offset + 0] := BMP_InfoHeader.Palette[b][2];
TextureData^[Offset + 1] := BMP_InfoHeader.Palette[b][1];
TextureData^[Offset + 2] := BMP_InfoHeader.Palette[b][0];
Inc(x);
if (x >= BMP_InfoHeader.Width) then
begin
x := 0;
Dec(y);
end;
end;
end;
if (BMP_InfoHeader.BitsPerPixel in [BMP_COLOR_24, BMP_COLOR_32]) then
begin
BMP_File.Seek(BMP_FileHeader.BitmapDataOffset, soFromBeginning);
while BMP_File.Position < BMP_File.Size do
begin
if BMP_InfoHeader.BitsPerPixel = BMP_COLOR_24 then
BMP_File.Read(BMP_Buffer, SizeOf(TBMP_Buffer24))
else
BMP_File.Read(BMP_Buffer, SizeOf(TBMP_Buffer32));
// Bitmap data is stored upside down in the file, so we calculate an offset
// to the texture data on
Offset := ((y * BMP_InfoHeader.Width) + x) * 3;
TextureData^[Offset + 0] := BMP_Buffer[2];
TextureData^[Offset + 1] := BMP_Buffer[1];
TextureData^[Offset + 2] := BMP_Buffer[0];
Inc(x);
if (x >= BMP_InfoHeader.Width) then
begin
x := 0;
Dec(y);
end;
end;
end;
BMP_File.Free;
Result := TRUE;
except
on E: Exception do
begin
if TextureData <> nil then
FreeMem(TextureData, Texture^.Size);
BMP_File.Free;
raise ExtendedException.Create('uTextureFormat', 'Load_BMP', E);
end;
end;
end;

function Load_PCX(Filename: String; var TextureData: PTextureData; var Texture: PTexture): Boolean;

type
TPCX_Header = packed record
Manufacturer,
Version,
Encoding,
BitsPerPixel: Byte;
XMin, YMin, XMax, YMax: SmallInt;
HRes, VRes: SmallInt;
Palette: array [0..47] of Byte;
Reserved,
ColorPlanes: Byte;
BytesPerLine,
PaletteType: Word;
Filler: array [0..57] of Byte;
end;

TPCX_Palette = array [0..255, 0..2] of Byte;

var
PCX_Header: TPCX_Header;
PCX_Palette: TPCX_Palette;
PCX_Data: PTextureData;
PCX_DataSize: LongWord;
PCX_File: TMemoryStream;
IsPalette: Byte;
x, y, Count, PCX_Count: LongWord;
DataByte, Len: Byte;
begin
Result := FALSE;
PCX_Data := nil;
PCX_DataSize := 0;
PCX_File := TMemoryStream.Create();
PCX_File.LoadFromFile(Filename);
try
PCX_File.Read(PCX_Header, SizeOf(TPCX_Header));
// Check for bad PCX file
if not ((PCX_Header.Manufacturer <> $0A) or (PCX_Header.version <> 5) or (PCX_Header.Encoding <> 1) or (PCX_Header.BitsPerPixel <> 8) or (PCX_Header.XMax >= 640) or (PCX_Header.YMax >= 480)) then
begin
PCX_File.Seek(128, soFromBeginning);
PCX_DataSize := (PCX_File.Size - SizeOf(TPCX_Header)) - 769;
GetMem(PCX_Data, PCX_DataSize);
PCX_File.Read(PCX_Data^, PCX_DataSize);
PCX_File.Seek(PCX_File.Size - 769, soFromBeginning);
PCX_File.Read(IsPalette, SizeOf(Byte));
PCX_File.Read(PCX_Palette, 768);
Texture^.Width := PCX_Header.XMax + 1;
Texture^.Height := PCX_Header.YMax + 1;
Texture^.Size := Texture^.Width * Texture^.Height * 3;
Texture^.GL_Type := GL_RGB;
GetMem(TextureData, Texture^.Size);
Count := 0;
PCX_Count := 0;
for y := 0 to PCX_Header.YMax + 1 do
begin
x := 0;
while x < PCX_Header.XMax do
begin
if PCX_Count > PCX_DataSize then
Break;
DataByte := PCX_Data^[PCX_Count];
Inc(PCX_Count);
if (DataByte and $C0) = $C0 then
begin
Len := (DataByte and $3F);
DataByte := PCX_Data^[PCX_Count];
Inc(PCX_Count);
end
else
Len := 1;
while Len > 0 do
begin
if (Count + 2) > Texture^.Size then
Break;
TextureData^[Count] := PCX_Palette[DataByte, 0];
TextureData^[Count + 1] := PCX_Palette[DataByte, 1];
TextureData^[Count + 2] := PCX_Palette[DataByte, 2];
Inc(Count, 3);
Inc(x);
Dec(Len);
end;
end;
end;
if (PCX_Data <> nil) and (PCX_DataSize > 0) then
FreeMem(PCX_Data, PCX_DataSize);
Result := TRUE;
end;
PCX_File.Free;
except
on E: Exception do
begin
if (PCX_Data <> nil) and (PCX_DataSize > 0) then
FreeMem(PCX_Data, PCX_DataSize);
PCX_File.Free;
raise ExtendedException.Create('uTextureFormat', 'Load_PCX', E);
end;
end;
end;


EDIT: Fixed a bug in the PCX code. :)