Moved program icon drawing into handmade layer.

This commit is contained in:
2026-05-29 15:24:46 -07:00
parent 94e20cdff5
commit 1ba8acfe51
5 changed files with 48 additions and 169 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 256 KiB

+15 -2
View File
@@ -123,7 +123,7 @@ static void DrawBitmap(game_offscreen_buffer *Buffer, loaded_bitmap *Bitmap, F32
U32 *Dest = (U32 *)DestRow; U32 *Dest = (U32 *)DestRow;
U32 *Source = SourceRow; U32 *Source = SourceRow;
for(S32 X = MinX; X < MaxX; X++) { for(S32 X = MinX; X < MaxX; X++) {
F32 A = (F32)((*Source >> 24) & 0xFF) / 255.0f; F32 SA = (F32)((*Source >> 24) & 0xFF);
F32 SR = (F32)((*Source >> 16) & 0xFF); F32 SR = (F32)((*Source >> 16) & 0xFF);
F32 SG = (F32)((*Source >> 8) & 0xFF); F32 SG = (F32)((*Source >> 8) & 0xFF);
F32 SB = (F32)((*Source >> 0) & 0xFF); F32 SB = (F32)((*Source >> 0) & 0xFF);
@@ -132,12 +132,14 @@ static void DrawBitmap(game_offscreen_buffer *Buffer, loaded_bitmap *Bitmap, F32
F32 DG = (F32)((*Dest >> 8) & 0xFF); F32 DG = (F32)((*Dest >> 8) & 0xFF);
F32 DB = (F32)((*Dest >> 0) & 0xFF); F32 DB = (F32)((*Dest >> 0) & 0xFF);
F32 A = SA / 255.0f;
// TODO: This should be replaced by premultiplied alpha. // TODO: This should be replaced by premultiplied alpha.
F32 R = (1.0f-A)*DR + A*SR; F32 R = (1.0f-A)*DR + A*SR;
F32 G = (1.0f-A)*DG + A*SG; F32 G = (1.0f-A)*DG + A*SG;
F32 B = (1.0f-A)*DB + A*SB; F32 B = (1.0f-A)*DB + A*SB;
*Dest = (((U32)RoundF32toS32(R) << 16) | ((U32)RoundF32toS32(G) << 8) | ((U32)RoundF32toS32(B) << 0)); *Dest = (((U32)RoundF32toS32(SA) << 24) | ((U32)RoundF32toS32(R) << 16) | ((U32)RoundF32toS32(G) << 8) | ((U32)RoundF32toS32(B) << 0));
Dest++; Dest++;
Source++; Source++;
@@ -234,6 +236,17 @@ void UpdateAndRender(thread_context *Thread, game_memory *Memory, game_input *In
GameState = (game_state *)Memory->PermanentStorage; GameState = (game_state *)Memory->PermanentStorage;
if(!Memory->IsInitialized) { 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"); GameState->Backdrop = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_background.bmp");
hero_bitmaps *Bitmap; hero_bitmaps *Bitmap;
+10 -6
View File
@@ -3,7 +3,6 @@
#include "core.h" #include "core.h"
#include "handmade_tile.h" #include "handmade_tile.h"
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
/* NOTE: Servcies that the platform layer provides to the game. */ /* NOTE: Servcies that the platform layer provides to the game. */
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
@@ -23,13 +22,15 @@ B32 DEBUGPlatformWriteEntireFile(thread_context *Thread, const char *Filename, U
void DEBUGPlatformFreeFileMemory(thread_context *Thread, void *Memory); void DEBUGPlatformFreeFileMemory(thread_context *Thread, void *Memory);
#endif #endif
typedef struct game_offscreen_buffer game_offscreen_buffer;
void SetProgramIcon(game_offscreen_buffer *ProgramIcon);
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
/* NOTE: Servcies that the game provides to the platform layer. */ /* NOTE: Servcies that the game provides to the platform layer. */
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
typedef struct game_offscreen_buffer { typedef struct game_offscreen_buffer {
/* Pixels are always 32-bit and have the bytes in BGRX /* Pixels are always 32-bit and have the bytes in BGRX (little-endian). */
* (little-endian). */
void *Memory; void *Memory;
S32 Width; S32 Width;
S32 Height; S32 Height;
@@ -112,6 +113,7 @@ typedef struct game_memory {
debug_read_file_result (*DEBUGPlatformReadEntireFile)(thread_context *Thread,const char *); debug_read_file_result (*DEBUGPlatformReadEntireFile)(thread_context *Thread,const char *);
B32 (*DEBUGPlatformWriteEntireFile)(thread_context *Thread, const char *, U32, void *); B32 (*DEBUGPlatformWriteEntireFile)(thread_context *Thread, const char *, U32, void *);
void (*DEBUGPlatformFreeFileMemory)(thread_context *Thread, void *); void (*DEBUGPlatformFreeFileMemory)(thread_context *Thread, void *);
void (*SetProgramIcon)(game_offscreen_buffer *ProgramIcon);
#endif #endif
} game_memory; } game_memory;
@@ -123,9 +125,7 @@ typedef struct game_memory {
#if OS_WINDOWS #if OS_WINDOWS
__declspec(dllexport) __declspec(dllexport)
#endif #endif
void UpdateAndRender(thread_context *Thread, game_memory *Memory, void UpdateAndRender(thread_context *Thread, game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer, game_sound_output_buffer *SoundBuffer);
game_input *Input, game_offscreen_buffer *Buffer,
game_sound_output_buffer *SoundBuffer);
/* /*
* *
@@ -177,6 +177,9 @@ typedef struct hero_bitmaps {
} hero_bitmaps; } hero_bitmaps;
typedef struct game_state { typedef struct game_state {
game_offscreen_buffer ProgramIcon;
loaded_bitmap ProgramIconBMP;
memory_arena WorldArena; memory_arena WorldArena;
world *World; world *World;
@@ -184,6 +187,7 @@ typedef struct game_state {
tile_map_position PlayerP; tile_map_position PlayerP;
v2 dPlayerP; // velocity v2 dPlayerP; // velocity
loaded_bitmap Backdrop; loaded_bitmap Backdrop;
U32 FacingDirection; U32 FacingDirection;
+21 -158
View File
@@ -56,6 +56,8 @@ SDL_GameController *ControllerHandles[MAX_CONTROLLERS];
static offscreen_buffer GlobalBackbuffer; static offscreen_buffer GlobalBackbuffer;
SDL_Window *LastWindow;
static B32 IsFullscreen; static B32 IsFullscreen;
static sdl_window_size WindowSize; static sdl_window_size WindowSize;
static sdl_window_position WindowPosition; static sdl_window_position WindowPosition;
@@ -138,6 +140,20 @@ void DEBUGPlatformFreeFileMemory(thread_context *Thread, void *Memory)
free(Memory); // TODO: Should we accept debug_read_file_result instead of just memory. free(Memory); // TODO: Should we accept debug_read_file_result instead of just memory.
} }
void SetProgramIcon(game_offscreen_buffer *ProgramIcon)
{
SDL_Window *Window = LastWindow;
SDL_Surface *Icon = SDL_CreateRGBSurfaceWithFormatFrom((void *)ProgramIcon->Memory, ProgramIcon->Width, ProgramIcon->Height, ProgramIcon->BytesPerPixel * 8, ProgramIcon->BytesPerPixel * ProgramIcon->Width, SDL_PIXELFORMAT_ARGB8888);
if(!Icon) {
/* TODO: This didn't work . . . SDL_GetError() */
return ;
}
SDL_SetWindowIcon(Window, Icon);
SDL_FreeSurface(Icon);
}
static sdl_border_size SDLGetWindowBorderSizes(SDL_Window *Window) static sdl_border_size SDLGetWindowBorderSizes(SDL_Window *Window)
{ {
sdl_border_size Result; sdl_border_size Result;
@@ -620,163 +636,6 @@ U64 __rdtsc()
} }
#endif #endif
/* TODO: In the future, this should just accept either a bitmap or image
* path. This is to be determined. One cool feature this might
* support is displaying certain information in the program icon by
* drawing into a bitmap. */
#include "handmade_intrinsics.h"
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 DEBUGLoadBMPP(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 SDLSetProgramIcon(SDL_Window *Window, game_offscreen_buffer *ProgramIcon)
{
SDL_Surface *icon = SDL_CreateRGBSurfaceWithFormatFrom((void *)ProgramIcon->Memory, ProgramIcon->Width, ProgramIcon->Height, ProgramIcon->BytesPerPixel * 8, ProgramIcon->BytesPerPixel * ProgramIcon->Width, SDL_PIXELFORMAT_ARGB8888);
if(icon) {
SDL_SetWindowIcon(Window, icon);
SDL_FreeSurface(icon);
} else {
/* TODO: This didn't work . . . SDL_GetError() */
}
}
#include "handmade_intrinsics.h"
S32 main(S32 argc, char *argv[]) S32 main(S32 argc, char *argv[])
{ {
sdl_state SDLState; sdl_state SDLState;
@@ -799,8 +658,9 @@ S32 main(S32 argc, char *argv[])
* the window once everything is ready. */ * the window once everything is ready. */
// TODO: SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, in non-debug build // TODO: SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, in non-debug build
SDL_Window *Window = SDL_CreateWindow("Handmade Hero", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, RESOLUTION_WIDTH+20, RESOLUTION_HEIGHT+20, SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE/* | SDL_WINDOW_ALWAYS_ON_TOP*/); SDL_Window *Window = SDL_CreateWindow("Handmade Hero", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, RESOLUTION_WIDTH+20, RESOLUTION_HEIGHT+20, SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE/* | SDL_WINDOW_ALWAYS_ON_TOP*/);
if(Window) { if(Window) {
LastWindow = Window;
SDL_DisplayMode display_mode; SDL_DisplayMode display_mode;
SDL_GetCurrentDisplayMode(0, &display_mode); // Various display information. SDL_GetCurrentDisplayMode(0, &display_mode); // Various display information.
@@ -822,6 +682,7 @@ S32 main(S32 argc, char *argv[])
// This way, we can use all the drawing functions of handmade. // This way, we can use all the drawing functions of handmade.
// https://learn.microsoft.com/en-us/windows/apps/design/iconography/app-icon-construction // https://learn.microsoft.com/en-us/windows/apps/design/iconography/app-icon-construction
// Probably, the icon size should be 256x256, so windows scales it properly. This buffer should be allocated into the arena. // Probably, the icon size should be 256x256, so windows scales it properly. This buffer should be allocated into the arena.
#if 0
loaded_bitmap ProgramIcon = DEBUGLoadBMPP(&TempContext, DEBUGPlatformReadEntireFile, "data/program_icon.bmp"); loaded_bitmap ProgramIcon = DEBUGLoadBMPP(&TempContext, DEBUGPlatformReadEntireFile, "data/program_icon.bmp");
game_offscreen_buffer ProgramIconBuffer; game_offscreen_buffer ProgramIconBuffer;
ProgramIconBuffer.Memory = malloc((ProgramIcon.Width * ProgramIcon.Height) * 4); ProgramIconBuffer.Memory = malloc((ProgramIcon.Width * ProgramIcon.Height) * 4);
@@ -833,6 +694,7 @@ S32 main(S32 argc, char *argv[])
DrawBitmap(&ProgramIconBuffer, &ProgramIcon, 0, 0, 0, 0); DrawBitmap(&ProgramIconBuffer, &ProgramIcon, 0, 0, 0, 0);
SDLSetProgramIcon(Window, &ProgramIconBuffer); SDLSetProgramIcon(Window, &ProgramIconBuffer);
#endif
// TODO: Make it a global. // TODO: Make it a global.
SDL_Cursor *PointerCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); SDL_Cursor *PointerCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
@@ -880,6 +742,7 @@ S32 main(S32 argc, char *argv[])
GameMemory.PermanentStorageSize = MB(64); GameMemory.PermanentStorageSize = MB(64);
GameMemory.TransientStorageSize = GB(1); GameMemory.TransientStorageSize = GB(1);
GameMemory.DEBUGPlatformReadEntireFile = &DEBUGPlatformReadEntireFile; GameMemory.DEBUGPlatformReadEntireFile = &DEBUGPlatformReadEntireFile;
GameMemory.SetProgramIcon = &SetProgramIcon;
//GameMemory.DEBUGPlatformWriteEntireFile = &DEBUGPlatformWriteEntireFile; //GameMemory.DEBUGPlatformWriteEntireFile = &DEBUGPlatformWriteEntireFile;
GameMemory.DEBUGPlatformFreeFileMemory = &DEBUGPlatformFreeFileMemory; GameMemory.DEBUGPlatformFreeFileMemory = &DEBUGPlatformFreeFileMemory;
+1 -2
View File
@@ -24,8 +24,7 @@ typedef struct sdl_window_position {
} sdl_window_position; } sdl_window_position;
typedef struct offscreen_buffer { typedef struct offscreen_buffer {
/* Pixels are always 32-bit and have the bytes in BGRX /* Pixels are always 32-bit and have the bytes in BGRX (little-endian). */
* (little-endian). */
SDL_Texture *Texture; SDL_Texture *Texture;
void *Memory; void *Memory;
S32 Width; S32 Width;