#include "core.h" #include "handmade.h" #include "handmade_math.h" #include "sdl_handmade.h" #include "handmade_intrinsics.h" #include "handmade_random.h" #include "handmade_tile.h" #include "handmade_tile.c" #include #include /* memset */ static void GameOutputSound(game_state *GameState, game_sound_output_buffer *SoundBuffer, S32 ToneHz) { /* S16 ToneVolume, *SampleOut; S32 WavePeriod, SampleIndex; ToneVolume = 0; WavePeriod = SoundBuffer->SamplesPerSecond / ToneHz; SampleOut = SoundBuffer->Samples; for(SampleIndex = 0; SampleIndex < SoundBuffer->SampleCount; SampleIndex++) { F32 SineValue; S16 SampleValue; SineValue = sinf(GameState->tSine); SampleValue = (S16)(SineValue * ToneVolume); *SampleOut++ = SampleValue; *SampleOut++ = SampleValue; GameState->tSine += 2.0f * (F32)PI * 1.0f / (F32)WavePeriod; if(GameState->tSine > 2.0f*(F32)PI) { GameState->tSine -= 2.0f*(F32)PI; } } */ } static void ClearBackground(game_offscreen_buffer *Buffer) { S32 Width = Buffer->Width; S32 Height = Buffer->Height; for(S32 X = 0; X < Width * Height; X++) { U32 *Pixel = &((U32 *)Buffer->Memory)[X]; *Pixel = 0xFFFFFF; } } static void DrawRectangle(game_offscreen_buffer *Buffer, v2 vMin, v2 vMax, F32 R, F32 G, F32 B) { S32 MinX = RoundF32toS32(vMin.X); S32 MinY = RoundF32toS32(vMin.Y); S32 MaxX = RoundF32toS32(vMax.X); S32 MaxY = RoundF32toS32(vMax.Y); S32 Width = Buffer->Width; S32 Height = Buffer->Height; if(MinX < 0) MinX = 0; if(MinY < 0) MinY = 0; if(MaxX > Width) MaxX = Width; if(MaxY > Height) MaxY = Height; U32 Color = (U32)((RoundF32toS32(R * 255.0f) << 16) | (RoundF32toS32(G * 255.0f) << 8) | (RoundF32toS32(B * 255.0f) << 0)); U8 *Row = ((U8 *)Buffer->Memory + MinX*Buffer->BytesPerPixel + MinY*Buffer->Pitch); for(S32 Y = MinY; Y < MaxY; Y++) { U32 *Pixel = (U32 *)Row; for(S32 X = MinX; X < MaxX; X++) { *(U32 *)Pixel = Color; Pixel++; } Row += Buffer->Pitch; } } static void DrawBitmap(game_offscreen_buffer *Buffer, loaded_bitmap *Bitmap, F32 RelX, F32 RelY, S32 AlignX, S32 AlignY) { RelX -= (F32)AlignX; RelY -= (F32)AlignY; S32 MinX = RoundF32toS32(RelX); S32 MinY = RoundF32toS32(RelY); S32 MaxX = RoundF32toS32(RelX + (F32)Bitmap->Width); S32 MaxY = RoundF32toS32(RelY + (F32)Bitmap->Height); S32 Width = Buffer->Width; S32 Height = Buffer->Height; S32 SourceOffsetX = 0; if(MinX < 0) { SourceOffsetX = -MinX; MinX = 0; } S32 SourceOffsetY = 0; if(MinY < 0) { SourceOffsetY = -MinY; MinY = 0; } if(MaxX > Width) MaxX = Width; if(MaxY > Height) MaxY = Height; U32 *SourceRow = Bitmap->Pixels + Bitmap->Width*(Bitmap->Height - 1); SourceRow += -Bitmap->Width*SourceOffsetY + SourceOffsetX; U8 *DestRow = ((U8 *)Buffer->Memory + MinX*Buffer->BytesPerPixel + MinY*Buffer->Pitch); for(S32 Y = MinY; Y < MaxY; Y++) { U32 *Dest = (U32 *)DestRow; U32 *Source = SourceRow; for(S32 X = MinX; X < MaxX; X++) { F32 SA = (F32)((*Source >> 24) & 0xFF); F32 SR = (F32)((*Source >> 16) & 0xFF); F32 SG = (F32)((*Source >> 8) & 0xFF); F32 SB = (F32)((*Source >> 0) & 0xFF); F32 DR = (F32)((*Dest >> 16) & 0xFF); F32 DG = (F32)((*Dest >> 8) & 0xFF); F32 DB = (F32)((*Dest >> 0) & 0xFF); F32 A = SA / 255.0f; // TODO: This should be replaced by premultiplied alpha. F32 R = (1.0f-A)*DR + A*SR; F32 G = (1.0f-A)*DG + A*SG; F32 B = (1.0f-A)*DB + A*SB; *Dest = (((U32)RoundF32toS32(SA) << 24) | ((U32)RoundF32toS32(R) << 16) | ((U32)RoundF32toS32(G) << 8) | ((U32)RoundF32toS32(B) << 0)); Dest++; Source++; } DestRow += Buffer->Pitch; SourceRow -= Bitmap->Width; } } #pragma pack(push, 1) typedef struct bitmap_header { U16 FileType; U32 FileSize; U16 Reserved1; U16 Reserved2; U32 BitmapOffset; U32 Size; S32 Width; S32 Height; U16 Planes; U16 BitsPerPixel; U32 Compression; U32 SizeOfBitmap; S32 HorzResolution; S32 VertResolution; U32 ColorsUsed; U32 ColorsImportant; U32 RedMask; U32 GreenMask; U32 BlueMask; } bitmap_header; #pragma pack(pop) static loaded_bitmap DEBUGLoadBMP(thread_context *Thread, debug_read_file_result (*DEBUGPlatformReadEntireFile)(thread_context *Thread, const char *), char *FileName) { loaded_bitmap Result; memset(&Result, 0, sizeof(Result)); debug_read_file_result ReadResult = DEBUGPlatformReadEntireFile(Thread, FileName); if(ReadResult.ContentsSize > 0) { bitmap_header *Header = (bitmap_header *)ReadResult.Contents; U32 *Pixels = (U32 *)((U8 *)ReadResult.Contents + Header->BitmapOffset); Result.Pixels = Pixels; Result.Width = Header->Width; Result.Height = Header->Height; ASSERT(Header->Compression == 3); // NOTE: The byte order in memory is determined by the Header. U32 RedMask = Header->RedMask; U32 GreenMask = Header->GreenMask; U32 BlueMask = Header->BlueMask; U32 AlphaMask = ~(RedMask | GreenMask | BlueMask); bit_scan_result RedShift = FindLeastSignificantSetBit(RedMask); bit_scan_result GreenShift = FindLeastSignificantSetBit(GreenMask); bit_scan_result BlueShift = FindLeastSignificantSetBit(BlueMask); bit_scan_result AlphaShift = FindLeastSignificantSetBit(AlphaMask); ASSERT(RedShift.Found); ASSERT(GreenShift.Found); ASSERT(BlueShift.Found); ASSERT(AlphaShift.Found); U32 *SourceDest = Pixels; for(S32 Y = 0; Y < Header->Height; Y++) { for(S32 X = 0; X < Header->Width; X++) { U32 C = *SourceDest; *SourceDest = ((((C >> AlphaShift.Index) & 0xFF) << 24) | (((C >> RedShift.Index ) & 0xFF) << 16) | (((C >> GreenShift.Index) & 0xFF) << 8) | (((C >> BlueShift.Index ) & 0xFF) << 0)); SourceDest++; } } } return Result; } void UpdateAndRender(thread_context *Thread, game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer, game_sound_output_buffer *SoundBuffer) { ASSERT((&Input->Controllers[0].Terminator - &Input->Controllers[0].Buttons[0]) == ARRAY_SIZE(Input->Controllers[0].Buttons)); ASSERT(sizeof(game_state) <= Memory->PermanentStorageSize); game_state *GameState; F32 PlayerHeight = 1.4f; F32 PlayerWidth = 0.75f * PlayerHeight; GameState = (game_state *)Memory->PermanentStorage; if(!Memory->IsInitialized) { GameState->ProgramIcon.Width = 256; GameState->ProgramIcon.Height = 256; GameState->ProgramIcon.BytesPerPixel = 4; GameState->ProgramIcon.Pitch = GameState->ProgramIcon.BytesPerPixel * GameState->ProgramIcon.Width; GameState->ProgramIcon.Memory = malloc(GameState->ProgramIcon.BytesPerPixel * GameState->ProgramIcon.Width * GameState->ProgramIcon.Height); memset(GameState->ProgramIcon.Memory, 0, GameState->ProgramIcon.BytesPerPixel * GameState->ProgramIcon.Width * GameState->ProgramIcon.Height); GameState->ProgramIconBMP = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/program_icon.bmp"); DrawBitmap(&GameState->ProgramIcon, &GameState->ProgramIconBMP, 0.0f, 0.0f, 0, 0); Memory->SetProgramIcon(&GameState->ProgramIcon); GameState->Backdrop = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_background.bmp"); hero_bitmaps *Bitmap; Bitmap = &GameState->HeroBitmaps[0]; Bitmap->AlignX = 72; Bitmap->AlignY = 182; Bitmap->Head = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_right_head.bmp"); Bitmap->Cape = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_right_cape.bmp"); Bitmap->Torso = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_right_torso.bmp"); Bitmap++; Bitmap->AlignX = 72; Bitmap->AlignY = 182; Bitmap->Head = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_back_head.bmp"); Bitmap->Cape = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_back_cape.bmp"); Bitmap->Torso = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_back_torso.bmp"); Bitmap++; Bitmap->AlignX = 72; Bitmap->AlignY = 182; Bitmap->Head = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_left_head.bmp"); Bitmap->Cape = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_left_cape.bmp"); Bitmap->Torso = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_left_torso.bmp"); Bitmap++; Bitmap->AlignX = 72; Bitmap->AlignY = 182; Bitmap->Head = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_front_head.bmp"); Bitmap->Cape = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_front_cape.bmp"); Bitmap->Torso = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_hero_front_torso.bmp"); GameState->CameraP.AbsTileX = 17 / 2; GameState->CameraP.AbsTileY = 9 / 2; GameState->PlayerP.AbsTileX = 2; GameState->PlayerP.AbsTileY = 3; GameState->PlayerP.Offset.X = 5.0f; GameState->PlayerP.Offset.Y = 5.0f; InitializeArena(&GameState->WorldArena, Memory->PermanentStorageSize - sizeof(game_state), (U8 *)Memory->PermanentStorage + sizeof(game_state)); GameState->World = PUSH_STRUCT(&GameState->WorldArena, world); world *World = GameState->World; World->TileMap = PUSH_STRUCT(&GameState->WorldArena, tile_map); tile_map *TileMap = World->TileMap; TileMap->ChunkShift = 4; TileMap->ChunkMask = (1 << TileMap->ChunkShift) - 1; TileMap->ChunkDim = (1 << TileMap->ChunkShift); TileMap->TileChunkCountX = 128; TileMap->TileChunkCountY = 128; TileMap->TileChunkCountZ = 2; TileMap->TileChunks = PUSH_ARRAY(&GameState->WorldArena, TileMap->TileChunkCountX * TileMap->TileChunkCountY * TileMap->TileChunkCountZ, tile_chunk); TileMap->TileSideInMeters = 1.4f; U32 RandomNumberIndex = 0; U32 TilesPerWidth = 17; U32 TilesPerHeight = 9; U32 ScreenX = 0; U32 ScreenY = 0; U32 AbsTileZ = 0; B32 DoorTop = FALSE; B32 DoorBottom = FALSE; B32 DoorLeft = FALSE; B32 DoorRight = FALSE; B32 DoorUp = FALSE; B32 DoorDown = FALSE; for(U32 ScreenIndex = 0; ScreenIndex < 100; ScreenIndex++) { ASSERT(RandomNumberIndex < ARRAY_SIZE(RandomNumberTable)); /* TODO: Random number generator. */ U32 RandomChoice; if(DoorUp || DoorDown) { RandomChoice = RandomNumberTable[RandomNumberIndex++] % 2; } else { RandomChoice = RandomNumberTable[RandomNumberIndex++] % 3; } B32 CreatedZDoor = FALSE; if(RandomChoice == 2) { CreatedZDoor = TRUE; if(AbsTileZ == 0) { DoorUp = TRUE; } else { DoorDown = TRUE; } } else if(RandomChoice == 1) { DoorRight = TRUE; } else { DoorTop = TRUE; } for(U32 TileY = 0; TileY < TilesPerHeight; TileY++) { for(U32 TileX = 0; TileX < TilesPerWidth; TileX++) { U32 AbsTileX = ScreenX * TilesPerWidth + TileX; U32 AbsTileY = ScreenY * TilesPerHeight + TileY; U32 TileValue = 1; if((TileX == 0) && (!DoorLeft || (TileY != (TilesPerHeight/2)))) { TileValue = 2; } if(((TileX == (TilesPerWidth - 1)) && (!DoorRight || (TileY != (TilesPerHeight/2))))) { TileValue = 2; } if((TileY == 0) && (!DoorBottom || (TileX != (TilesPerWidth/2)))) { TileValue = 2; } if((TileY == (TilesPerHeight - 1)) && (!DoorTop || (TileX != (TilesPerWidth/2)))) { TileValue = 2; } if((TileX == 10) && (TileY == 6)) { if(DoorUp) { TileValue = 3; } if(DoorDown) { TileValue = 4; } } SetTileValue(&GameState->WorldArena, World->TileMap, AbsTileX, AbsTileY, AbsTileZ, TileValue); } } DoorLeft = DoorRight; DoorBottom = DoorTop; if(CreatedZDoor) { DoorDown = !DoorDown; DoorUp = !DoorUp; } else { DoorUp = FALSE; DoorDown = FALSE; } DoorTop = FALSE; DoorRight = FALSE; if(RandomChoice == 2) { if(AbsTileZ == 0) { AbsTileZ = 1; } else { AbsTileZ = 0; } } else if(RandomChoice == 1) { ScreenX++; } else { ScreenY++; } } Memory->IsInitialized = TRUE; } world *World = GameState->World; tile_map *TileMap = World->TileMap; S32 TileSideInPixels = 60; F32 MetersToPixels = (F32)TileSideInPixels / TileMap->TileSideInMeters; tile_map_position OldPlayerP = GameState->PlayerP; for(U32 ControllerIndex = 0; ControllerIndex < ARRAY_SIZE(Input->Controllers); ControllerIndex++) { game_controller_input *Controller = GetController(Input, ControllerIndex); if(Controller->IsAnalog) { } else { v2 ddPlayer; // accerlation memset(&ddPlayer, 0, sizeof(ddPlayer)); if(Controller->MoveUp.EndedDown) { ddPlayer.Y = 1.0f; GameState->FacingDirection = 1; } if(Controller->MoveDown.EndedDown) { ddPlayer.Y = -1.0f; GameState->FacingDirection = 3; } if(Controller->MoveLeft.EndedDown) { ddPlayer.X = -1.0f; GameState->FacingDirection = 2; } if(Controller->MoveRight.EndedDown) { ddPlayer.X = 1.0f; GameState->FacingDirection = 0; } if((ddPlayer.X != 0.0f) && (ddPlayer.Y != 0.0f)) ddPlayer = V2Multiply(0.7071067811865475244f, ddPlayer); F32 PlayerSpeed = 10.0f; if(Controller->ActionUp.EndedDown) PlayerSpeed = 20.0f; ddPlayer = V2Multiply(PlayerSpeed, ddPlayer); ddPlayer = V2Add(ddPlayer, V2Unary(V2Multiply(1.5f, GameState->dPlayerP))); tile_map_position NewPlayerP = GameState->PlayerP; v2 PlayerDelta = V2Add(V2Multiply(0.5f, V2Multiply(Square(Input->dtForFrame), ddPlayer)), V2Multiply(Input->dtForFrame, GameState->dPlayerP)); NewPlayerP.Offset = V2Add(PlayerDelta, NewPlayerP.Offset); GameState->dPlayerP = V2Add(V2Multiply(Input->dtForFrame, ddPlayer), GameState->dPlayerP); NewPlayerP = RecannonicalizePosition(TileMap, NewPlayerP); #if 1 tile_map_position PlayerLeft; PlayerLeft = NewPlayerP; PlayerLeft.Offset.X -= 0.5f*PlayerWidth; PlayerLeft = RecannonicalizePosition(TileMap, PlayerLeft); tile_map_position PlayerRight; PlayerRight = NewPlayerP; PlayerRight.Offset.X += 0.5f*PlayerWidth; PlayerRight = RecannonicalizePosition(TileMap, PlayerRight); B32 Collided = FALSE; tile_map_position ColP; memset(&ColP, 0, sizeof(ColP)); if(!IsTileMapPointEmpty(TileMap, NewPlayerP)) { ColP = NewPlayerP; Collided = TRUE; } if(!IsTileMapPointEmpty(TileMap, PlayerLeft)) { ColP = PlayerLeft; Collided = TRUE; } if(!IsTileMapPointEmpty(TileMap, PlayerRight)) { ColP = PlayerRight; Collided = TRUE; } if(Collided) { v2 r = { 0, 0 }; if(ColP.AbsTileX < GameState->PlayerP.AbsTileX) { r = (v2){ 1, 0 }; } if(ColP.AbsTileX > GameState->PlayerP.AbsTileX) { r = (v2){ -1, 0 }; } if(ColP.AbsTileY < GameState->PlayerP.AbsTileY) { r = (v2){ 0, 1 }; } if(ColP.AbsTileY > GameState->PlayerP.AbsTileY) { r = (v2){ 0, -1 }; } GameState->dPlayerP = V2Subtract(GameState->dPlayerP, V2Multiply(1*InnerProduct(GameState->dPlayerP, r), r)); } else { GameState->PlayerP = NewPlayerP; } #else U32 MinTileX = 0; U32 MinTileY = 0; U32 OnePastMaxTileX = 0; U32 OnePastMaxTileY = 0; U32 AbsTileZ = GameState->PlayerP.AbsTileZ; tile_map_position BestPlayerP = GameState->PlayerP; F32 BestDistance = LengthSq(PlayerDelta); for(U32 AbsTileY = MinTileY; AbsTileY != OnePastMaxTileY; AbsTileY++) { for(U32 AbsTileX = MinTileX; AbsTileX != OnePastMaxTileX; AbsTileX++) { tile_map_position TestTileP = CenteredTilePoint(AbsTileX, AbsTileY, AbsTileZ); U32 TileValue = GetTileValueByPos(TileMap, TestTileP); if(IsTileValueEmpty(TileValue)) { v2 MinCorner = V2Multiply(-0.5f, (v2){ TileMap->TileSideInMeters, TileMap->TileSideInMeters }); v2 MaxCorner = V2Multiply(-0.5f, (v2){ TileMap->TileSideInMeters, TileMap->TileSideInMeters }); tile_map_difference RelNewPlayerP = SubtractInF32(TileMap, &TestTileP, &NewPlayerP); v2 TestP = ClosestPointInRectangle(MinCorner, MaxCorner, RelNewPlayerP); if(...) { } } } } #endif } } if(!AreOnSameTile(&OldPlayerP, &GameState->PlayerP)) { U32 NewTileValue = GetTileValueByPos(TileMap, GameState->PlayerP); if(NewTileValue == 3) { GameState->PlayerP.AbsTileZ++; } else if(NewTileValue == 4) { GameState->PlayerP.AbsTileZ--; } } GameState->CameraP.AbsTileZ = GameState->PlayerP.AbsTileZ; tile_map_difference Diff = SubtractInF32(TileMap, &GameState->PlayerP, &GameState->CameraP); if(Diff.dXY.X > (9.0f*TileMap->TileSideInMeters)) { GameState->CameraP.AbsTileX += 17; } if(Diff.dXY.X < -(9.0f*TileMap->TileSideInMeters)) { GameState->CameraP.AbsTileX -= 17; } if(Diff.dXY.Y > (5.0f*TileMap->TileSideInMeters)) { GameState->CameraP.AbsTileY += 9; } if(Diff.dXY.Y < -(5.0f*TileMap->TileSideInMeters)) { GameState->CameraP.AbsTileY -= 9; } Diff = SubtractInF32(TileMap, &GameState->PlayerP, &GameState->CameraP); //DrawRectangle(Buffer, 0.0f, 0.0f, (F32)Buffer->Width, (F32)Buffer->Height, 0.0f, 1.0f, 0.0f); DrawBitmap(Buffer, &GameState->Backdrop, 0.0f, 0.0f, 0, 0); F32 ScreenCenterX = 0.5f * (F32)Buffer->Width; F32 ScreenCenterY = 0.5f * (F32)Buffer->Height; for(S32 RelRow = -10; RelRow < 10; RelRow++) { for(S32 RelColumn = -20; RelColumn < 20; RelColumn++) { U32 Column = GameState->CameraP.AbsTileX + RelColumn; U32 Row = GameState->CameraP.AbsTileY + RelRow; U32 TileID = GetTileValue(TileMap, Column, Row, GameState->CameraP.AbsTileZ); if(TileID > 1) { F32 Gray = 0.5f; if(TileID == 2) { Gray = 1.0f; } if(TileID > 2) { Gray = 0.25f; } if((Column == GameState->CameraP.AbsTileX) && (Row == GameState->CameraP.AbsTileY)) { Gray = 0.0f; } v2 TileSide = { 0.5f*(F32)TileSideInPixels, 0.5f*(F32)TileSideInPixels }; v2 Center = { ScreenCenterX - MetersToPixels*GameState->CameraP.Offset.X + (F32)RelColumn*(F32)TileSideInPixels, ScreenCenterY + MetersToPixels*GameState->CameraP.Offset.Y - (F32)RelRow*(F32)TileSideInPixels }; v2 Min = V2Subtract(Center, TileSide); v2 Max = V2Add(Center, TileSide); DrawRectangle(Buffer, Min, Max, Gray, Gray, Gray); } } } F32 PlayerR = 1.0f; F32 PlayerG = 0.0f; F32 PlayerB = 0.0f; F32 PlayerGroundPointX = ScreenCenterX + MetersToPixels*Diff.dXY.X; F32 PlayerGroundPointY = ScreenCenterY - MetersToPixels*Diff.dXY.Y; v2 PlayerLeftTop = { PlayerGroundPointX - 0.5f * MetersToPixels*PlayerWidth, PlayerGroundPointY - MetersToPixels*PlayerHeight }; v2 PlayerWidthHeight = { PlayerWidth, PlayerHeight }; DrawRectangle(Buffer, PlayerLeftTop, V2Add(PlayerLeftTop, V2Multiply(MetersToPixels, PlayerWidthHeight)), PlayerR, PlayerG, PlayerB); hero_bitmaps *Bitmap = &GameState->HeroBitmaps[GameState->FacingDirection]; DrawBitmap(Buffer, &Bitmap->Torso, PlayerGroundPointX, PlayerGroundPointY, Bitmap->AlignX, Bitmap->AlignY); DrawBitmap(Buffer, &Bitmap->Cape, PlayerGroundPointX, PlayerGroundPointY, Bitmap->AlignX, Bitmap->AlignY); DrawBitmap(Buffer, &Bitmap->Head, PlayerGroundPointX, PlayerGroundPointY, Bitmap->AlignX, Bitmap->AlignY); GameOutputSound(GameState, SoundBuffer, 400); } /* static void RenderWeirdGradient(game_offscreen_buffer *Buffer, S32 XOffset, S32 YOffset) { S32 Width, Height, Y; U8 *Row; Width = Buffer->Width; Height = Buffer->Height; Row = (U8 *)Buffer->Memory; for(Y = 0; Y < Height; Y++) { U32 *Pixel = (U32 *)Row; S32 X; Pixel = (U32 *)Row; for(X = 0; X < Width; X++) { U8 Blue, Green; Blue = (U8)(X + XOffset); Green = (U8)(Y + YOffset); *Pixel = (Green << 8) | Blue; Pixel++; } Row += Buffer->Pitch; } } */