diff --git a/data/program_icon.bmp b/data/program_icon.bmp new file mode 100644 index 0000000..e58c43e Binary files /dev/null and b/data/program_icon.bmp differ diff --git a/sdl_handmade.c b/sdl_handmade.c index a2f7e8f..b49c167 100644 --- a/sdl_handmade.c +++ b/sdl_handmade.c @@ -595,32 +595,175 @@ U64 __rdtsc() * 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. */ -void SDLSetProgramIcon(SDL_Window *Window) -{ -#if 0 - U32 Rmask, Gmask, Bmask, Amask; -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - S32 shift_by = (program_icon.bytes_per_pixel == 3) ? 8 : 0; - Rmask = 0xff000000 >> shift_by; - Gmask = 0x00ff0000 >> shift_by; - Bmask = 0x0000ff00 >> shift_by; - Amask = 0x000000ff >> shift_by; -#else - Rmask = 0x000000ff; - Gmask = 0x0000ff00; - Bmask = 0x00ff0000; - Amask = (program_icon.bytes_per_pixel == 3) ? 0 : 0xff000000; -#endif - SDL_Surface * icon = SDL_CreateRGBSurfaceFrom((void *)program_icon.pixel_data, program_icon.width, program_icon.height, - program_icon.bytes_per_pixel * 8, program_icon.bytes_per_pixel * program_icon.width, Rmask, Gmask, Bmask, Amask); +#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 A = (F32)((*Source >> 24) & 0xFF) / 255.0f; + 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); + + // 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(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++; + } + } + +#if 0 + U32 *Beginning = Pixels; + U32 *Ending = Pixels + (Header->Width * Header->Height); + for(S32 Y = 0; Y < Header->Height; Y++) { + for(S32 X = 0; X < Header->Width; X++) { + U32 Temp = *Beginning; + *Beginning = *Ending; + *Ending = Temp; + + Beginning++; + Ending--; + } + } +#endif + } + + return Result; +} + +void SDLSetProgramIcon(SDL_Window *Window, game_offscreen_buffer *ProgramIcon) +{ + U32 Rmask, Gmask, Bmask, Amask; + Rmask = 0x00ff0000; + Gmask = 0x0000ff00; + Bmask = 0x000000ff; + Amask = 0xff000000; + + SDL_Surface *icon = SDL_CreateRGBSurfaceFrom((void *)ProgramIcon->Memory, ProgramIcon->Width, ProgramIcon->Height, ProgramIcon->BytesPerPixel * 8, ProgramIcon->BytesPerPixel * ProgramIcon->Width, Rmask, Gmask, Bmask, Amask); if(icon) { SDL_SetWindowIcon(Window, icon); SDL_FreeSurface(icon); } else { /* TODO: This didn't work . . . SDL_GetError() */ } -#endif } #include "handmade_intrinsics.h" @@ -662,7 +805,19 @@ S32 main(S32 argc, char *argv[]) * its actual size. */ /* SDL_RenderPresent(Renderer) is enough to display the window. */ if(Renderer) { - SDLSetProgramIcon(Window); + thread_context TempContext; // TODO: + memset(&TempContext, 0, sizeof(TempContext)); + + loaded_bitmap ProgramIcon = DEBUGLoadBMPP(&TempContext, DEBUGPlatformReadEntireFile, "data/program_icon.bmp"); + game_offscreen_buffer ProgramIconBuffer; + ProgramIconBuffer.Memory = malloc((ProgramIcon.Width * ProgramIcon.Height) * 4); + ProgramIconBuffer.Width = ProgramIcon.Width; + ProgramIconBuffer.Height = ProgramIcon.Height; + ProgramIconBuffer.BytesPerPixel = 4; + ProgramIconBuffer.Pitch = 4 * ProgramIcon.Width; + + DrawBitmap(&ProgramIconBuffer, &ProgramIcon, 0, 0, 0, 0); + SDLSetProgramIcon(Window, &ProgramIconBuffer); // TODO: Make it a global. SDL_Cursor *PointerCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);