Skip to content

Instantly share code, notes, and snippets.

@pJotoro
Last active April 2, 2023 18:38
Show Gist options
  • Save pJotoro/fb2f19514f91cbcf8c390b26bf7c7d5a to your computer and use it in GitHub Desktop.
Save pJotoro/fb2f19514f91cbcf8c390b26bf7c7d5a to your computer and use it in GitHub Desktop.
package kit
//////////////////////////////////////////////////////////////////////////////
// Embedded font
//////////////////////////////////////////////////////////////////////////////
@(private)
font_png := [?]byte{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x01, 0x00,
0x01, 0x03, 0x00, 0x00, 0x00, 0xdf, 0x8d, 0xeb, 0x44, 0x00, 0x00, 0x00,
0x06, 0x50, 0x4c, 0x54, 0x45, 0x47, 0x70, 0x4c, 0xff, 0xff, 0xff, 0x9f,
0x94, 0xa2, 0x43, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00,
0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x05, 0x4d, 0x49, 0x44, 0x41, 0x54,
0x58, 0xc3, 0xed, 0x98, 0x3f, 0x88, 0x24, 0x45, 0x14, 0xc6, 0xdf, 0x74,
0x37, 0xdd, 0xd5, 0xbb, 0x75, 0xd3, 0x13, 0x4e, 0x70, 0x48, 0x7b, 0x98,
0x08, 0x06, 0x03, 0x06, 0x2e, 0xb8, 0x4a, 0xbb, 0xd1, 0x04, 0x07, 0x5e,
0x62, 0xde, 0x82, 0x5c, 0x28, 0x03, 0x82, 0x0c, 0xc7, 0xa1, 0x4f, 0x5c,
0xb8, 0x0b, 0x56, 0x9c, 0xf0, 0x44, 0x11, 0x03, 0x95, 0xc5, 0x4c, 0x30,
0x50, 0x30, 0xa8, 0x03, 0x83, 0xe3, 0x18, 0xf5, 0xc2, 0x0d, 0xd7, 0xdc,
0x60, 0xc2, 0x09, 0x46, 0xcf, 0xef, 0x55, 0x55, 0x77, 0x4f, 0xcf, 0x1f,
0x6f, 0x11, 0x5d, 0xf7, 0x8e, 0x2b, 0x8a, 0xde, 0x9e, 0xfe, 0xf5, 0xab,
0x7a, 0xaf, 0x5e, 0x7d, 0xd5, 0x55, 0x4b, 0xb4, 0xb5, 0x24, 0x3c, 0xae,
0xab, 0xfb, 0x49, 0x84, 0x7b, 0xdc, 0xb2, 0x6a, 0xaa, 0xfb, 0x49, 0xb8,
0x7f, 0x72, 0xc0, 0xf6, 0xc8, 0xff, 0xc7, 0x32, 0x5e, 0xf6, 0x6a, 0xa9,
0x92, 0x6a, 0xc5, 0xd1, 0xd4, 0x27, 0x06, 0x6c, 0x8d, 0xfc, 0xe2, 0x96,
0x4e, 0x9e, 0xc7, 0xa7, 0xf8, 0x9b, 0xab, 0xfa, 0x11, 0xdc, 0xd5, 0x34,
0xe9, 0xe4, 0xa3, 0x34, 0x1f, 0x2b, 0x2a, 0x22, 0xca, 0x5b, 0x20, 0x99,
0x1d, 0xa7, 0xc5, 0x54, 0x91, 0x89, 0xe9, 0xd8, 0xb7, 0x41, 0x16, 0x04,
0xc5, 0x69, 0x5c, 0xfc, 0x6c, 0xc1, 0x28, 0x58, 0x02, 0x04, 0x40, 0xfc,
0x26, 0x59, 0x0b, 0x07, 0x02, 0x07, 0xb2, 0xd3, 0x11, 0xf1, 0x2f, 0x16,
0xe4, 0x19, 0x13, 0xaa, 0x07, 0x11, 0xef, 0x75, 0x06, 0x53, 0x0b, 0xc8,
0x59, 0x44, 0xae, 0x2b, 0x01, 0xc3, 0x35, 0xa0, 0xe5, 0xe6, 0x38, 0xa8,
0x00, 0xae, 0xa8, 0xaa, 0x02, 0xa3, 0x60, 0x00, 0x59, 0x14, 0xd1, 0x32,
0xb0, 0xc5, 0xc5, 0xe5, 0x22, 0x2f, 0xb6, 0x0d, 0xd0, 0x45, 0x48, 0xde,
0x38, 0xa6, 0x71, 0x66, 0x74, 0x66, 0xf6, 0x32, 0xd3, 0x4c, 0x27, 0xdc,
0x4c, 0xbb, 0x74, 0xd4, 0xe1, 0xcb, 0x9a, 0x4a, 0x32, 0x53, 0x8d, 0xe0,
0xa8, 0x2f, 0x01, 0x1a, 0x9a, 0xc6, 0xa4, 0x43, 0x7a, 0x41, 0x23, 0x60,
0x0f, 0x06, 0x24, 0x2f, 0x59, 0x90, 0xf0, 0xd5, 0x8c, 0xe7, 0x64, 0xc6,
0x16, 0x94, 0x99, 0x19, 0x74, 0x58, 0x40, 0x9f, 0xcc, 0x11, 0x5e, 0xef,
0x30, 0x2c, 0x8c, 0x98, 0x52, 0x3f, 0x24, 0x01, 0x03, 0x32, 0x7f, 0x02,
0x84, 0x34, 0x4d, 0x04, 0x88, 0x85, 0x03, 0x68, 0x51, 0x3b, 0x40, 0x75,
0x1f, 0x16, 0xc0, 0x07, 0xad, 0x2c, 0xe8, 0x30, 0x5e, 0x11, 0xaf, 0x42,
0x1b, 0xc7, 0x22, 0x61, 0xf4, 0x3f, 0x0e, 0xa1, 0x15, 0xb1, 0x95, 0x12,
0xae, 0x07, 0x1c, 0x6d, 0x1b, 0x09, 0x75, 0x31, 0x54, 0x93, 0xf0, 0x1c,
0x35, 0x33, 0x88, 0x66, 0xaa, 0x49, 0xeb, 0xd2, 0x28, 0xfd, 0x01, 0xf2,
0xf1, 0xaa, 0xc6, 0x68, 0x17, 0x78, 0x64, 0xec, 0x55, 0xcb, 0x38, 0x5e,
0xbe, 0x8b, 0x61, 0x7e, 0x4d, 0x00, 0x59, 0x20, 0x57, 0x7d, 0x89, 0x4d,
0x77, 0xff, 0x37, 0x80, 0xcf, 0x8e, 0xda, 0x20, 0x23, 0x93, 0xdd, 0xfc,
0x16, 0xe0, 0xcb, 0x8f, 0xcd, 0x1c, 0x8f, 0x90, 0xa8, 0x4b, 0x66, 0x01,
0xd0, 0x25, 0xb3, 0xfb, 0xfb, 0x27, 0x00, 0x5f, 0x37, 0x16, 0xf6, 0x06,
0x16, 0xe9, 0xf4, 0x23, 0x80, 0x4f, 0x75, 0x1b, 0xa0, 0x0f, 0x75, 0x74,
0x0b, 0xa0, 0x68, 0xbc, 0x72, 0x6f, 0x18, 0xb8, 0xfb, 0x21, 0x00, 0xdc,
0x75, 0x71, 0x18, 0x1b, 0xc7, 0x58, 0x97, 0x73, 0x17, 0xc7, 0xe3, 0x51,
0xba, 0xc4, 0x5d, 0x2b, 0x62, 0x09, 0x22, 0xa1, 0x85, 0x44, 0x37, 0x45,
0x74, 0x18, 0x2e, 0x8e, 0xe9, 0x9a, 0xcc, 0x44, 0x79, 0xc4, 0x83, 0x0a,
0x68, 0x4d, 0x45, 0x4c, 0xb7, 0x2b, 0x40, 0x2b, 0x80, 0x3c, 0xe8, 0x36,
0xa0, 0xaf, 0x29, 0x77, 0x60, 0x2e, 0x63, 0xe5, 0x01, 0x34, 0x38, 0xa8,
0x81, 0x0c, 0x6a, 0x58, 0xf5, 0x11, 0x52, 0xa9, 0xa9, 0xd7, 0x80, 0x4e,
0x03, 0x4c, 0x0b, 0xa4, 0x02, 0xee, 0xe9, 0x7b, 0x0e, 0x28, 0x0f, 0x90,
0x06, 0xf4, 0x21, 0x29, 0x99, 0x01, 0x2c, 0x1c, 0xc8, 0x66, 0x6e, 0xe1,
0x6a, 0x45, 0x2e, 0xf5, 0xc2, 0x16, 0xb6, 0xfe, 0x42, 0xdd, 0x7b, 0x12,
0x2c, 0xf5, 0x75, 0xe5, 0x7e, 0x51, 0x81, 0xd2, 0x45, 0x54, 0x83, 0xdc,
0x66, 0xf0, 0x66, 0xc2, 0xb3, 0x44, 0xa6, 0x7d, 0x1f, 0x53, 0x34, 0xfb,
0x7e, 0x2e, 0x33, 0x51, 0x49, 0x06, 0x91, 0x89, 0xd2, 0x66, 0xac, 0x8f,
0x49, 0x6d, 0x13, 0x03, 0x0d, 0xfa, 0xb9, 0xec, 0x01, 0x64, 0xe0, 0x81,
0x97, 0x45, 0x05, 0xf4, 0x0a, 0xc0, 0xfa, 0xb5, 0xc9, 0x02, 0x7d, 0x14,
0x1b, 0xfa, 0x70, 0x5e, 0x95, 0x89, 0x6b, 0xca, 0x08, 0x68, 0xeb, 0x43,
0x02, 0xb8, 0xb3, 0x69, 0xa5, 0x12, 0x30, 0xda, 0xb8, 0x76, 0xfc, 0x47,
0x4b, 0xd2, 0xda, 0xa3, 0xc0, 0x6d, 0x17, 0x2b, 0x50, 0x2e, 0x2d, 0xaf,
0x1c, 0xfd, 0x3d, 0x98, 0x27, 0xe6, 0x24, 0xa1, 0x59, 0xad, 0x28, 0x0b,
0xde, 0x02, 0xc0, 0x8f, 0xb9, 0xe6, 0xb2, 0x02, 0xca, 0x82, 0x3b, 0x0e,
0x98, 0x2e, 0xd5, 0xa0, 0x87, 0xfd, 0x50, 0x60, 0x9b, 0x12, 0x90, 0x78,
0x80, 0x39, 0x9f, 0xd7, 0x7d, 0x08, 0x08, 0x9b, 0xa6, 0x8a, 0x16, 0x48,
0x05, 0x88, 0x3e, 0x70, 0xbf, 0xec, 0x95, 0x11, 0x7d, 0x98, 0x19, 0xf4,
0x91, 0x78, 0xaf, 0x24, 0x72, 0xd3, 0xd2, 0x87, 0xa9, 0x22, 0xa7, 0x36,
0x50, 0xe6, 0xec, 0x63, 0xf7, 0x74, 0xbf, 0xfb, 0xd8, 0xee, 0x77, 0x9f,
0xe6, 0xe3, 0xc2, 0x9f, 0x3f, 0xe0, 0x92, 0xf3, 0xca, 0x1f, 0x2d, 0x12,
0x3a, 0x21, 0x7a, 0x78, 0x4a, 0x0b, 0x59, 0x93, 0xec, 0x31, 0x63, 0x14,
0x32, 0xe9, 0x01, 0x2b, 0x3e, 0x09, 0x0d, 0xcb, 0x5b, 0xbc, 0xef, 0xc0,
0x71, 0x5c, 0x50, 0x5c, 0xb2, 0x1c, 0x12, 0x72, 0x00, 0x58, 0x1c, 0xb8,
0x85, 0xec, 0x8b, 0x98, 0x0e, 0xb3, 0x19, 0x3e, 0xfc, 0x94, 0x4e, 0xf8,
0x59, 0x98, 0xf0, 0x0f, 0xce, 0x02, 0x00, 0xdf, 0x61, 0xde, 0x25, 0xda,
0xb9, 0xc6, 0x57, 0x0a, 0x58, 0x7c, 0xe5, 0x9b, 0xca, 0x68, 0x81, 0x8f,
0x6b, 0xca, 0xb4, 0xd3, 0xe3, 0xe7, 0xb8, 0xb1, 0x18, 0x61, 0x9f, 0x0a,
0x0b, 0xcd, 0x62, 0xf1, 0x3c, 0x35, 0x16, 0xf8, 0xe4, 0x7f, 0x07, 0x10,
0xb3, 0xf4, 0x61, 0x2d, 0x3c, 0xa0, 0x38, 0xff, 0x03, 0x20, 0xb4, 0x5e,
0xd9, 0x3e, 0x0e, 0x3c, 0xc8, 0xcc, 0x8d, 0xbb, 0xcc, 0xd8, 0x60, 0x23,
0x0e, 0x78, 0x95, 0xf0, 0x7e, 0x2b, 0x1f, 0x4a, 0x7e, 0xd8, 0xc8, 0x57,
0x87, 0xe8, 0x7c, 0xd2, 0x50, 0x2b, 0xa6, 0xf5, 0x1d, 0x20, 0xfe, 0xe6,
0xf8, 0xb6, 0xdd, 0x6e, 0xdb, 0x75, 0x74, 0x12, 0xd1, 0x50, 0x4b, 0x8a,
0xde, 0x23, 0x7e, 0xe3, 0xe0, 0x4a, 0xe9, 0x00, 0x82, 0x9d, 0x28, 0x9a,
0x58, 0x70, 0xdf, 0x5a, 0x4c, 0x6a, 0x80, 0x5d, 0xd0, 0x09, 0x8c, 0xe2,
0xf2, 0xbe, 0xb5, 0x98, 0xa4, 0x55, 0x53, 0x18, 0x92, 0x13, 0x18, 0x59,
0x00, 0x8b, 0x32, 0x62, 0x6f, 0xe1, 0x4b, 0x5c, 0xde, 0xb0, 0x16, 0x38,
0x34, 0xae, 0x82, 0x1d, 0xe7, 0x55, 0x27, 0x17, 0x10, 0xb7, 0x41, 0x63,
0xa1, 0xd7, 0x2d, 0x5c, 0x1f, 0xba, 0x3e, 0xe2, 0x25, 0x0f, 0x76, 0x12,
0x6b, 0x91, 0x52, 0x5b, 0x1f, 0xf6, 0xd8, 0xe1, 0x22, 0xff, 0xf7, 0x8f,
0x1d, 0x45, 0x87, 0x06, 0x71, 0x31, 0x0c, 0xdd, 0x3e, 0x88, 0x07, 0x11,
0xe3, 0xe3, 0x25, 0x43, 0x9e, 0x07, 0x34, 0x4c, 0x79, 0x18, 0x89, 0x1b,
0x79, 0x87, 0x70, 0x83, 0x40, 0x30, 0x62, 0xd4, 0x8b, 0xe8, 0xd0, 0x9e,
0x79, 0x71, 0xe9, 0x05, 0x9b, 0x56, 0xb8, 0xf7, 0x6f, 0xf1, 0x02, 0xa7,
0x90, 0xcc, 0x9e, 0xca, 0x62, 0xbf, 0xad, 0x75, 0xb5, 0x30, 0xf6, 0x4c,
0xb1, 0x0e, 0xe8, 0x6c, 0xc0, 0xb5, 0x8b, 0xfa, 0x50, 0xd3, 0x1c, 0xa7,
0x90, 0x8c, 0xff, 0x71, 0x53, 0x8f, 0xf6, 0xea, 0xd7, 0x95, 0x38, 0x9a,
0x12, 0x6e, 0xfd, 0xc7, 0xc5, 0x36, 0x10, 0x9c, 0x3d, 0x25, 0xb9, 0x4d,
0x89, 0x33, 0xea, 0xd5, 0x3d, 0x8d, 0xc3, 0xbc, 0xe7, 0x52, 0x62, 0xbb,
0xc9, 0x23, 0x1e, 0x3a, 0x7d, 0xa8, 0x68, 0x7d, 0x03, 0x61, 0x27, 0xb5,
0x52, 0xbd, 0xfa, 0x9f, 0x70, 0xcf, 0x1c, 0xd9, 0x98, 0x64, 0xee, 0x1a,
0x52, 0xba, 0xe7, 0x63, 0x24, 0x7a, 0x51, 0x57, 0x20, 0x2e, 0x49, 0x75,
0x2b, 0x10, 0x15, 0x6d, 0xb0, 0x5b, 0x81, 0x80, 0x5f, 0x6a, 0x00, 0x9a,
0x4a, 0x27, 0x1e, 0x74, 0xe8, 0xe5, 0x96, 0x85, 0x7a, 0x50, 0x5b, 0xb4,
0x9b, 0x52, 0xa3, 0x2d, 0x7d, 0xa8, 0x41, 0xed, 0xd5, 0x3b, 0xcb, 0x60,
0xbc, 0x14, 0xc7, 0xe7, 0xf5, 0xf7, 0xa3, 0x6b, 0xce, 0xf3, 0xd3, 0xb0,
0x9e, 0xf0, 0x1c, 0x62, 0xa9, 0x64, 0x20, 0x22, 0xaa, 0x41, 0x0f, 0x62,
0x81, 0xa2, 0x9c, 0x70, 0x02, 0x11, 0x8e, 0x28, 0x0a, 0xc2, 0x51, 0x01,
0x1d, 0x22, 0x7d, 0xb6, 0x29, 0x11, 0x51, 0xad, 0xa8, 0x47, 0xef, 0x19,
0xa0, 0x28, 0x77, 0xe3, 0x67, 0xe2, 0x91, 0x32, 0xbe, 0x4e, 0x0b, 0x37,
0x68, 0x1e, 0xdc, 0x4c, 0x8c, 0xaf, 0x53, 0x6a, 0x81, 0x7a, 0x52, 0xbf,
0xae, 0x69, 0x9b, 0x38, 0xb7, 0xe9, 0xe3, 0x6a, 0xf1, 0x93, 0x96, 0xba,
0xda, 0xc7, 0xaa, 0x57, 0xe7, 0xa4, 0x8f, 0x2d, 0x63, 0xb5, 0x9c, 0x86,
0xea, 0x5f, 0xce, 0x76, 0x7f, 0x15, 0x17, 0x92, 0x06, 0xa4, 0xc4, 0x29,
0x23, 0xa6, 0x6a, 0xc3, 0x92, 0xb2, 0xa4, 0xc1, 0x29, 0x0a, 0x8f, 0xd2,
0x1a, 0xac, 0x69, 0xa9, 0x02, 0x59, 0x35, 0xfb, 0x02, 0x7a, 0xa5, 0xd5,
0x54, 0x3d, 0x56, 0x44, 0xd7, 0x37, 0x83, 0x8c, 0xaf, 0x6f, 0xb3, 0x78,
0x7b, 0x33, 0x08, 0xe8, 0xdd, 0x33, 0xf5, 0x31, 0xd6, 0xa6, 0xd6, 0xc7,
0x8f, 0xb8, 0xc6, 0x74, 0x7e, 0xfb, 0xab, 0xbf, 0x00, 0xec, 0x51, 0xe8,
0xd2, 0x17, 0x57, 0x55, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
0x44, 0xae, 0x42, 0x60, 0x82,
}
// NOTE: This doesn't actually work. Nothing draws. I'm putting this up here for those who are curious, and also (to be honest) so
// someone might point out a silly mistake I'm making.
package kit
import "core:intrinsics"
@(private)
L :: intrinsics.constant_utf16_cstring
import "core:image"
import "core:image/png"
import "core:bytes"
import "core:slice"
import "core:math"
import "core:mem"
import "core:time"
import win32 "core:sys/windows"
Flag :: enum {
Scale_2x,
Scale_3x,
Scale_4x,
Hide_Cursor,
FPS_30,
FPS_144,
FPS_INF,
}
Flags :: distinct bit_set[Flag]
Color :: image.RGBA_Pixel
WHITE := Color{0xff, 0xff, 0xff, 0xff}
BLACK := Color{0, 0, 0, 0xff}
Rect :: struct {
x, y, w, h: int,
}
Image :: image.Image
Glyph :: struct {
rect: Rect,
xadv: int,
}
Font :: struct {
image: ^Image,
glyphs: [256]Glyph,
}
Key_State :: enum u8 {
Down,
Pressed,
Released,
}
Key_States :: distinct bit_set[Key_State; u8]
Screen :: struct {
pixels: []Color,
w, h: int,
}
Context :: struct {
wants_quit: bool,
hide_cursor: bool,
// input
char_buf: [32]rune,
key_state: [256]Key_States,
mouse_state: [16]Key_States,
mouse_pos: struct {x, y: int},
mouse_delta: struct {x, y: int},
// time
step_time: time.Duration,
prev_time: time.Duration,
// graphics
clip: Rect,
font: Font,
screen: Screen,
// windows
win_w, win_h: int,
hwnd: win32.HWND,
hdc: win32.HDC,
}
BIG_RECT :: Rect{0, 0, 0xffffff, 0xffffff}
create :: proc(title: string, w: int, h: int, flags: Flags) -> ^Context {
ctx := new(Context)
ctx.screen = Screen{
w = w,
h = h,
pixels = make([]Color, w * h),
}
ctx.step_time = flags_to_step_time(flags)
ctx.hide_cursor = .Hide_Cursor in flags
ctx.clip = Rect{0, 0, w, h}
instance := win32.HANDLE(win32.GetModuleHandleW(L("")))
IDC_ARROW :: cast([^]u16)rawptr(uintptr(32512))
window_class := win32.WNDCLASSEXW{
cbSize = size_of(win32.WNDCLASSEXW),
lpfnWndProc = wndproc,
hInstance = instance,
hCursor = win32.LoadCursorW(instance, IDC_ARROW),
lpszClassName = win32.utf8_to_wstring(title),
hIcon = win32.LoadIconW(instance, L("icon")),
}
win32.RegisterClassExW(&window_class)
w := w
h := h
scale_size_by_flags(&w, &h, flags)
rect := win32.RECT{right = i32(w), bottom = i32(h)}
style := win32.WS_OVERLAPPEDWINDOW
win32.AdjustWindowRect(&rect, style, false)
ctx.hwnd = win32.CreateWindowExW(0, window_class.lpszClassName, window_class.lpszClassName, style, win32.CW_USEDEFAULT, win32.CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, nil, nil, instance, nil)
SetPropW(ctx.hwnd, L("Context"), win32.HANDLE(ctx))
win32.ShowWindow(ctx.hwnd, win32.SW_NORMAL)
ctx.hdc = win32.GetDC(ctx.hwnd)
ctx.font = load_font(font_png[:])
ctx.prev_time = now()
return ctx
}
destroy :: proc(using ctx: ^Context) {
win32.ReleaseDC(hwnd, hdc)
win32.DestroyWindow(hwnd)
free(ctx)
}
step :: proc(using ctx: ^Context, dt: ^time.Duration = nil) -> bool {
RedrawWindow(hwnd, nil, nil, RDW_INVALIDATE|RDW_UPDATENOW)
_now := now()
wait := prev_time + step_time - _now
prev := prev_time
if wait > 0 {
win32.timeBeginPeriod(1)
time.accurate_sleep(wait)
win32.timeEndPeriod(1)
prev_time += step_time
} else {
prev_time = _now
}
if dt != nil do dt^ = prev_time - prev
slice.fill(char_buf[:], 0)
for key in &key_state {
key -= {.Pressed, .Released}
}
for key in &mouse_state {
key -= {.Pressed, .Released}
}
msg: win32.MSG
for win32.PeekMessageW(&msg, hwnd, 0, 0, win32.PM_REMOVE) {
win32.TranslateMessage(&msg)
win32.DispatchMessageW(&msg)
}
return !wants_quit
}
load_font_file :: proc(filename: string) -> Font {
img, _ := image.load(filename)
return load_font_from_image(img)
}
load_font_mem :: proc(data: []byte) -> Font {
img, err := png.load(data)
return load_font_from_image(img)
}
load_font :: proc{load_font_file, load_font_mem}
destroy_font :: proc(font: Font) {
image.destroy(font.image)
}
text_width :: proc(font: ^Font, text: string) -> int {
x := 0
for r in text {
x += font.glyphs[r].xadv
}
return x
}
get_char :: proc "contextless" (using ctx: ^Context) -> rune {
for c, i in char_buf {
if c == 0 do continue
char_buf[i] = 0
return c
}
return 0
}
key_down :: #force_inline proc "contextless" (using ctx: ^Context, key: int) -> bool {
return .Down in key_state[key]
}
key_pressed :: #force_inline proc "contextless" (using ctx: ^Context, key: int) -> bool {
return .Pressed in key_state[key]
}
key_released :: #force_inline proc "contextless" (using ctx: ^Context, key: int) -> bool {
return .Released in key_state[key]
}
mouse_pos :: #force_inline proc "contextless" (using ctx: ^Context) -> (x, y: int) {
x = mouse_pos.x
y = mouse_pos.y
return
}
mouse_delta :: #force_inline proc "contextless" (using ctx: ^Context) -> (x, y: int) {
x = mouse_delta.x
y = mouse_delta.y
return
}
mouse_down :: #force_inline proc "contextless" (using ctx: ^Context, button: int) -> bool {
return .Down in mouse_state[button]
}
mouse_pressed :: #force_inline proc "contextless" (using ctx: ^Context, button: int) -> bool {
return .Pressed in mouse_state[button]
}
mouse_released :: #force_inline proc "contextless" (using ctx: ^Context, button: int) -> bool {
return .Released in mouse_state[button]
}
clear :: proc "contextless" (using ctx: ^Context, color: Color) {
draw_rect(ctx, color, BIG_RECT)
}
set_clip :: proc "contextless" (using ctx: ^Context, rect: Rect) {
screen_rect := Rect{0, 0, screen.w, screen.h}
clip = intersect_rects(rect, screen_rect)
}
draw_point :: proc "contextless" (using ctx: ^Context, color: Color, x, y: int) {
if color.a == 0 do return
r := clip
if x < r.x || y < r.y || x >= r.x + r.w || y >= r.y + r.h do return
dst := &screen.pixels[x + y * screen.w]
dst^ = blend_pixel(dst^, color)
}
draw_rect :: proc "contextless" (using ctx: ^Context, color: Color, rect: Rect) {
if color.a == 0 do return
rect := rect
rect = intersect_rects(rect, clip)
d := &screen.pixels[rect.x + rect.y * screen.w]
dr := screen.w - rect.w
for y in 0..<rect.h {
for x in 0..<rect.w {
d^ = blend_pixel(d^, color)
d = intrinsics.ptr_offset(d, 1)
}
d = intrinsics.ptr_offset(d, dr)
}
}
draw_line :: proc "contextless" (using ctx: ^Context, color: Color, x1, y1, x2, y2: int) {
dx := abs(x2-x1)
sx := x1 < x2 ? 1 : -1
dy := -abs(y2 - y1)
sy := y1 < y2 ? 1 : -1
err := dx + dy
x1 := x1
y1 := y1
x2 := x2
y2 := y2
for {
draw_point(ctx, color, x1, y1)
if x1 == x2 && y1 == y2 do break
e2 := err << 1
if e2 >= dy {
err += dy
x1 += sx
}
if e2 <= dx {
err += dx
y1 += sy
}
}
}
draw_image1 :: proc "contextless" (using ctx: ^Context, img: ^Image, x, y: int) {
dst := Rect{x, y, img.width, img.height}
src := Rect{0, 0, img.width, img.height}
draw_image3(ctx, WHITE, BLACK, img, dst, src)
}
draw_image2 :: proc "contextless" (using ctx: ^Context, color: Color, img: ^Image, x, y: int, src: Rect) {
dst := Rect{x, y, abs(src.w), abs(src.h)}
draw_image3(ctx, color, BLACK, img, dst, src)
}
draw_image3 :: proc "contextless" (using ctx: ^Context, mul_color, add_color: Color, img: ^Image, dst, src: Rect) {
if src.w == 0 || src.h == 0 || dst.w == 0 || dst.h == 0 do return
cx1 := clip.x
cy1 := clip.y
cx2 := cx1 + clip.w
cy2 := cy1 + clip.h
stepx := (src.w << 10) / dst.w
stepy := (src.h << 10) / dst.h
sy := src.y << 10
dy := dst.y
if dy < cy1 {
sy += (cy1 - dy) * stepy
dy = cy1
}
ey := min(cy2, dst.y + dst.h)
blend_fn := 1
if transmute(u32)mul_color != 0xffffffff do blend_fn = 2
if (transmute(u32)add_color & 0xffffff00) != 0xffffff00 do blend_fn = 3
for ; dy < ey; dy += 1 {
if (dy >= cy1 && dy < cy2) {
sx := src.x << 10
pixels := image_pixels(img)
srow := pixels[(sy >> 10)*img.width:(sy >> 10)*img.width+img.width]
drow := screen.pixels[dy*screen.w:dy*screen.w+screen.w]
dx := dst.x
if dx < cx1 {
sx += (cx1 - dx) * stepx
dx = cx1
}
ex := min(cx2, dst.x + dst.w)
for ; dx < ex; dx += 1 {
s := &srow[sx >> 10]
d := &drow[dx]
switch blend_fn {
case 1:
d^ = blend_pixel(d^, s^)
case 2:
d^ = blend_pixel(d^, s^, mul_color)
case 3:
d^ = blend_pixel(d^, s^, mul_color, add_color)
}
sx += stepx
}
}
sy += stepy
}
}
@(private)
image_pixels :: #force_inline proc "contextless" (img: ^Image) -> []Color {
s: mem.Raw_Slice
s.data = &img.pixels.buf[0]
s.len = len(img.pixels.buf) / 4
return transmute([]Color)s
}
draw_image :: proc{draw_image1, draw_image2, draw_image3}
draw_text1 :: proc "contextless" (ctx: ^Context, color: Color, text: string, x, y: int) -> int {
return draw_text2(ctx, color, ctx.font, text, x, y)
}
draw_text2 :: proc "contextless" (ctx: ^Context, color: Color, font: Font, text: string, x, y: int) -> int {
x := x
for r in text {
g := font.glyphs[r]
draw_image2(ctx, color, font.image, x, y, g.rect)
x += g.xadv
}
return x
}
draw_text :: proc{draw_text1, draw_text2}
@(private)
wndproc :: proc "stdcall" (hwnd: win32.HWND, message: win32.UINT, w_param: win32.WPARAM, l_param: win32.LPARAM) -> win32.LRESULT {
ctx := cast(^Context)GetPropW(hwnd, L("Context"))
switch message {
case win32.WM_PAINT:
bmi := win32.BITMAPINFO{
bmiHeader = win32.BITMAPINFOHEADER{
biSize = size_of(win32.BITMAPINFOHEADER),
biBitCount = 32,
biCompression = win32.BI_RGB,
biPlanes = 1,
biWidth = i32(ctx.screen.w),
biHeight = i32(ctx.screen.h),
},
}
wr := get_adjusted_window_rect(ctx)
win32.StretchDIBits(ctx.hdc, i32(wr.x), i32(wr.y), i32(wr.w), i32(wr.h), 0, 0, i32(ctx.screen.w), i32(ctx.screen.h), &ctx.screen.pixels[0], &bmi, win32.DIB_RGB_COLORS, win32.SRCCOPY)
win32.ValidateRect(hwnd, nil)
case win32.WM_SETCURSOR:
if ctx.hide_cursor && win32.LOWORD(u32(l_param)) == win32.HTCLIENT {
win32.SetCursor(nil)
}
return win32.DefWindowProcW(hwnd, message, w_param, l_param)
case win32.WM_KEYDOWN, win32.WM_SYSKEYDOWN:
if l_param & (1 << 30) == 0 {
ctx.key_state[w_param] = {.Down, .Pressed}
}
case win32.WM_KEYUP, win32.WM_SYSKEYUP:
ctx.key_state[w_param] -= {.Down}
ctx.key_state[w_param] += {.Released}
case win32.WM_CHAR:
if w_param >= 32 {
for r in &ctx.char_buf {
if r != 0 do continue
r = rune(w_param)
break
}
}
case win32.WM_LBUTTONDOWN, win32.WM_LBUTTONUP, win32.WM_RBUTTONDOWN, win32.WM_RBUTTONUP, win32.WM_MBUTTONDOWN, win32.WM_MBUTTONUP:
button := (message == win32.WM_LBUTTONDOWN || message == win32.WM_LBUTTONUP) ? 1 : (message == win32.WM_RBUTTONDOWN || message == win32.WM_RBUTTONUP) ? 2 : 3
if message == win32.WM_LBUTTONDOWN || message == win32.WM_RBUTTONDOWN || message == win32.WM_MBUTTONDOWN {
win32.SetCapture(hwnd)
ctx.mouse_state[button] += {.Down, .Pressed}
} else {
win32.ReleaseCapture()
ctx.mouse_state[button] -= {.Down}
}
fallthrough
case win32.WM_MOUSEMOVE:
wr := get_adjusted_window_rect(ctx)
prevx := ctx.mouse_pos.x
prevy := ctx.mouse_pos.y
ctx.mouse_pos.x = (cast(int)win32.GET_X_LPARAM(l_param) - wr.x) * ctx.screen.w / wr.w
ctx.mouse_pos.y = (cast(int)win32.GET_Y_LPARAM(l_param) - wr.y) * ctx.screen.h / wr.h
ctx.mouse_delta.x += ctx.mouse_pos.x - prevx
ctx.mouse_delta.y += ctx.mouse_pos.y - prevy
case win32.WM_SIZE:
if w_param != win32.SIZE_MINIMIZED {
ctx.win_w = int(win32.LOWORD(u32(l_param)))
ctx.win_h = int(win32.HIWORD(u32(l_param)))
ps: win32.PAINTSTRUCT
hdc := win32.BeginPaint(hwnd, &ps)
brush := win32.CreateSolidBrush(win32.RGB(0, 0, 0))
win32.FillRect(hdc, &ps.rcPaint, brush)
win32.DeleteObject(win32.HGDIOBJ(brush))
win32.EndPaint(hwnd, &ps)
RedrawWindow(ctx.hwnd, nil, nil, RDW_INVALIDATE|RDW_UPDATENOW)
}
case win32.WM_QUIT, win32.WM_CLOSE:
ctx.wants_quit = true
case:
return win32.DefWindowProcW(hwnd, message, w_param, l_param)
}
return 0
}
@(private)
flags_to_step_time :: proc "contextless" (flags: Flags) -> time.Duration {
if .FPS_30 in flags do return 30 * time.Millisecond
if .FPS_144 in flags do return 144 * time.Millisecond
if .FPS_INF in flags do return 0
return 60 * time.Millisecond
}
@(private)
scale_size_by_flags :: proc "contextless" (w, h: ^int, flags: Flags) {
if .Scale_2x in flags {
w^ *= 2
h^ *= 2
} else if .Scale_3x in flags {
w^ *= 3
h^ *= 3
} else if .Scale_4x in flags {
w^ *= 4
h^ *= 4
}
}
@(private)
check_column :: proc "contextless" (img: ^Image, x, y, h: int) -> bool {
x := x
y := y
h := h
for h > 0 {
pixels := image_pixels(img)
a := pixels[x+y*img.width].a
if a != 0 do return true
y += 1
h -= 1
}
return false
}
@(private)
load_font_from_image :: proc "contextless" (img: ^Image) -> (font: Font) {
font.image = img
for glyph, i in &font.glyphs {
r := Rect{
(img.width / 16) * (i % 16),
(img.height / 16) * (i / 16),
img.width / 16,
img.height / 16,
}
for x := r.x + r.w - 1; x >= r.x; x -= 1 {
if check_column(font.image, x, r.y, r.h) do break
r.w -= 1
}
for x := r.x; x < r.x + r.w; x += 1 {
if check_column(font.image, x, r.y, r.h) do break
r.x += 1
r.w -= 1
}
glyph.xadv = r.w + 1
glyph.rect = r
}
font.glyphs[' '].rect = Rect{}
font.glyphs[' '].xadv = font.glyphs['a'].xadv
return
}
@(private)
intersect_rects :: #force_inline proc "contextless" (a, b: Rect) -> Rect {
x1 := max(a.x, b.x)
y1 := max(a.y, b.y)
x2 := min(a.x + a.w, b.x + b.w)
y2 := min(a.y + a.h, b.y + b.h)
return Rect{x1, y1, x2 - x1, y2 - y1}
}
@(private)
blend_pixel1 :: #force_inline proc "contextless" (dst, src: Color) -> Color {
res: Color
res_w: u32
dst_w := transmute(u32)dst
src_w := transmute(u32)src
res_w = (dst_w & 0xff00ff) + ((((src_w & 0xff00ff) - (dst_w & 0xff00ff)) * u32(src.a)) >> 8)
res = transmute(Color)res_w
res.g = dst.g + (((src.g - dst.g) * src.a) >> 8)
res.a = dst.a
return res
}
@(private)
blend_pixel2 :: #force_inline proc "contextless" (dst: Color, src: Color, clr: Color) -> Color {
dst := dst
src := src
clr := clr
src.a = (src.a * clr.a) >> 8
ia := 0xff - src.a
dst.r = ((src.r * clr.r * src.a) >> 16) + ((dst.r * ia) >> 8)
dst.g = ((src.g * clr.g * src.a) >> 16) + ((dst.g * ia) >> 8)
dst.b = ((src.b * clr.b * src.b) >> 16) + ((dst.b * ia) >> 8)
return dst
}
@(private)
blend_pixel3 :: #force_inline proc "contextless" (dst, src, clr, add: Color) -> Color {
src := src
src.r = min(255, src.r + add.r)
src.g = min(255, src.g + add.g)
src.b = min(255, src.b + add.b)
return blend_pixel2(dst, src, clr)
}
@(private)
blend_pixel :: proc{blend_pixel1, blend_pixel2, blend_pixel3}
@(private)
get_adjusted_window_rect :: proc "contextless" (using ctx: ^Context) -> Rect {
src_ar := f32(screen.h) / f32(screen.w)
dst_ar := f32(win_h) / f32(win_w)
w, h: int
if src_ar < dst_ar {
w = win_w
h = int(math.ceil(f32(w) * src_ar))
} else {
h = win_h
w = int(math.ceil(f32(h) / src_ar))
}
return Rect{(win_w - w) / 2, (win_h - h) / 2, w, h}
}
@(private)
now :: proc "contextless" () -> time.Duration {
_now := time.tick_now()
return time.Duration(_now._nsec)
}
package kit
import win32 "core:sys/windows"
foreign import user32 "system:User32.lib"
@(default_calling_convention="stdcall", private)
foreign user32 {
SetPropW :: proc(hWnd: win32.HWND, lpString: win32.LPCWSTR, hData: win32.HANDLE) -> win32.BOOL ---
RedrawWindow :: proc(hWnd: win32.HWND, lprcUpdate: win32.LPRECT, hrgnUpdate: win32.HRGN, flags: win32.UINT) -> win32.BOOL ---
GetPropW :: proc(hWnd: win32.HWND, lpString: win32.LPCWSTR) -> win32.HANDLE ---
}
/*
* RedrawWindow() flags
*/
/*
#define RDW_INVALIDATE 0x0001
#define RDW_INTERNALPAINT 0x0002
#define RDW_ERASE 0x0004
#define RDW_VALIDATE 0x0008
#define RDW_NOINTERNALPAINT 0x0010
#define RDW_NOERASE 0x0020
#define RDW_NOCHILDREN 0x0040
#define RDW_ALLCHILDREN 0x0080
#define RDW_UPDATENOW 0x0100
#define RDW_ERASENOW 0x0200
#define RDW_FRAME 0x0400
#define RDW_NOFRAME 0x0800
*/
@(private)
RDW_INVALIDATE :: win32.UINT(0x0001)
@(private)
RDW_UPDATENOW :: win32.UINT(0x0100)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment