inline s32 RoundFloatToInt(float value) { return static_cast(value + 0.5); } struct V4: public ImVec4 { using ImVec4::ImVec4; float &a = ImVec4::w; float &r = ImVec4::x; float &g = ImVec4::y; float &b = ImVec4::z; }; inline u32 V4ToARGB(V4 c) { u32 result = ((RoundFloatToInt(c.a * 255.0) & 0xff) << 24 | (RoundFloatToInt(c.r * 255.0) & 0xff) << 16 | (RoundFloatToInt(c.g * 255.0) & 0xff) << 8 | (RoundFloatToInt(c.b * 255.0) & 0xff) << 0); return result; } struct OffscreenBuffer { s32 width; s32 height; u8 *pixels; s32 pitch; const int BytesPerPixel; }; inline void PutPixelUnchecked(OffscreenBuffer *buffer, s32 x, s32 y, V4 color) { u32 *pixel = reinterpret_cast(buffer->pixels + y * buffer->pitch + x * buffer->BytesPerPixel); *pixel = V4ToARGB(color); } inline void PutPixel(OffscreenBuffer *buffer, s32 x, s32 y, V4 color) { if (x >= 0 && x < buffer->width && y >= 0 && y < buffer->height) { PutPixelUnchecked(buffer, x, y, color); } } void DrawRectangle(OffscreenBuffer *buffer, float realMinX, float realMinY, float realMaxX, float realMaxY, V4 color) { s32 minX = RoundFloatToInt(realMinX); s32 minY = RoundFloatToInt(realMinY); s32 maxX = RoundFloatToInt(realMaxX); s32 maxY = RoundFloatToInt(realMaxY); if (minX < 0) minX = 0; if (minY < 0) minY = 0; if (maxX > buffer->width) maxX = buffer->width; if (maxY > buffer->height) maxY = buffer->height; u8 *row = buffer->pixels + minY * buffer->pitch; for (s32 y = minY; y < maxY; ++y) { u32 *dstPixel = reinterpret_cast(row + minX * buffer->BytesPerPixel); for (s32 x = minX; x < maxX; ++x) { float a = color.a; float srcR = color.r * 255.0; float srcG = color.g * 255.0; float srcB = color.b * 255.0; float dstR = ((*dstPixel >> 16) & 0xff); float dstG = ((*dstPixel >> 8) & 0xff); float dstB = ((*dstPixel >> 0) & 0xff); dstR = (1 - a) * dstR + a * srcR; dstG = (1 - a) * dstG + a * srcG; dstB = (1 - a) * dstB + a * srcB; *dstPixel = (RoundFloatToInt(dstR) << 16 | RoundFloatToInt(dstG) << 8 | RoundFloatToInt(dstB) << 0); ++dstPixel; } row += buffer->pitch; } }