Skip to content

Instantly share code, notes, and snippets.

@gszauer
Created October 14, 2021 02:19
Show Gist options
  • Save gszauer/fd16f62338decb053c2a7343eed128c6 to your computer and use it in GitHub Desktop.
Save gszauer/fd16f62338decb053c2a7343eed128c6 to your computer and use it in GitHub Desktop.
PolygonRenderer
#include "Polygon.h"
#include <list>
#include <algorithm>
#include <bitset>
#include <vector>
#include <list>
void DrawPixel(int x, int y, unsigned char r, unsigned char g, unsigned char b);
void DrawPixel(int x, int y, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
namespace WindowsDotHDefinesPolygonWhichIsAPain {
uint64_t CoverageTable[64][64];
bool CoverageTableInitialized = false;
void SetPixelInBitset(uint64_t& bitset, int x, int y) {
unsigned int bit = y * 8 + x;
bitset |= (uint64_t(1) << bit);
}
bool GetPixelInBitset(uint64_t& bitset, int x, int y) {
unsigned int bit = y * 8 + x;
return (bitset & (uint64_t(1) << bit)) != 0;
}
void InitCoverageTable() {
if (CoverageTableInitialized) {
return;
}
CoverageTableInitialized = true;
// Draw lines
for (int outer = 0; outer < 64; ++outer) {
for (int inner = 0; inner < 64; ++inner) {
CoverageTable[outer][inner] = 0;
int x0 = outer % 8;
int y0 = outer / 8;
int x1 = inner % 8;
int y1 = inner / 8;
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
int xStep = x0 < x1 ? 1 : -1;
int yStep = y0 < y1 ? 1 : -1;
int error = 0;
if (dx > dy) {
int m = 2 * dy;
int scale = 2 * dx;
for (int x = x0, y = y0; x != x1 + xStep; x += xStep) {
SetPixelInBitset(CoverageTable[outer][inner], x, y);
error += m;
if (error >= dx) {
y += yStep;
error -= scale;
}
}
}
else {
int m = 2 * dx;
int scale = 2 * dy;
for (int y = y0, x = x0; y != y1 + yStep; y += yStep) {
SetPixelInBitset(CoverageTable[outer][inner], x, y);
error += m;
if (error >= dy) {
x += xStep;
error -= scale;
}
}
}
}
}
// Flood fill right
for (int outer = 0; outer < 64; ++outer) {
for (int inner = 0; inner < 64; ++inner) {
for (int y = 0; y < 8; ++y) {
bool fill = false;
for (int x = 0; x < 8; ++x) {
if (fill) {
SetPixelInBitset(CoverageTable[outer][inner], x, y);
}
if (GetPixelInBitset(CoverageTable[outer][inner], x, y)) {
fill = true;
}
}
}
}
}
}
BoundingBox GetBoundingBox(const Polygon& poly) {
BoundingBox result { Point { 0.0f, 0.0f}, Point{0.0f, 0.0f} };
if (poly.size() > 0) {
if (poly[0].size() > 0) {
result.min = result.max = poly[0][0];
}
}
for (int c = 0; c < (int)poly.size(); ++c) {
const Contour& contour = poly[c];
for (int p = 0; p < (int)contour.size(); ++p) {
const Point& point = contour[p];
if (point.x < result.min.x) { result.min.x = point.x; }
if (point.y < result.min.y) { result.min.y = point.y; }
if (point.x > result.max.x) { result.max.x = point.x; }
if (point.y > result.max.y) { result.max.y = point.y; }
}
}
return result;
}
bool PointInPolygon(const Point& point, const Polygon& poly) {
BoundingBox bbox = GetBoundingBox(poly);
if (point.x < bbox.min.x || point.x > bbox.max.x) {
return false;
}
if (point.y < bbox.min.y || point.y > bbox.max.y) {
return false;
}
int winding = 0;
for (int contour = 0, numContours = poly.size(); contour < numContours; ++contour) {
const Contour& c = poly[contour];
for (int pt = 0, numPoints = c.size(); pt < numPoints; ++pt) {
const Point& p = c[pt];
const Point& n = c[(pt + 1) % numPoints];
Point toStart{ p.x - point.x, p.y - point.y };
Point toEnd{ n.x - point.x, n.y - point.y };
float crossZ = toStart.x * toEnd.y - toStart.y * toEnd.x;
if (p.y <= point.y && n.y > point.y) { // Crossing scanline top to bottom
if (crossZ > 0) { // Point is on right of edge
winding -= 1;
}
}
else if (n.y <= point.y && p.y > point.y) { // Crossing scanline bottom up
if (crossZ < 0) { // Point is on left of edge
winding += 1;
}
}
}
}
return winding != 0;
}
Polygon ClipPolygon(const Polygon& poly, const BoundingBox& box) {
unsigned int numContours = (int)poly.size();
Polygon bufferA, bufferB;
bufferA.resize(poly.size());
bufferB.resize(poly.size());
// Clip left
for (int contour = 0; contour < numContours; ++contour) {
const Contour& c = poly[contour];
bufferA[contour].clear();
int numPoints = (int)c.size();
for (int point = 0; point < numPoints; ++point) {
const Point& p = c[point];
if (p.x >= box.min.x) {
bufferA[contour].push_back(p);
}
const Point& n = c[(point + 1) % numPoints];
float min_x = p.x < n.x ? p.x : n.x;
float max_x = p.x > n.x ? p.x : n.x;
if (min_x <= box.min.x && max_x >= box.min.x) {
bool vertical = fabsf(n.x - p.x) < 0.0001f;
if (!vertical) {
Point clip{ box.min.x, p.y + (n.y - p.y) * (box.min.x - p.x) / (n.x - p.x) };
bufferA[contour].push_back(clip);
}
}
}
}
// Clip right
for (int contour = 0; contour < numContours; ++contour) {
const Contour& c = bufferA[contour];
bufferB[contour].clear();
for (int point = 0, numPoints = (int)c.size(); point < numPoints; ++point) {
const Point& p = c[point];
if (p.x <= box.max.x) {
bufferB[contour].push_back(p);
}
const Point& n = c[(point + 1) % numPoints];
float min_x = p.x < n.x ? p.x : n.x;
float max_x = p.x > n.x ? p.x : n.x;
if (min_x <= box.max.x && max_x >= box.max.x) {
bool vertical = fabsf(n.x - p.x) < 0.0001f;
if (!vertical) {
Point clip{ box.max.x, p.y + (n.y - p.y) * (box.max.x - p.x) / (n.x - p.x) };
bufferB[contour].push_back(clip);
}
}
}
}
// Clip top
for (int contour = 0; contour < numContours; ++contour) {
const Contour& c = bufferB[contour];
bufferA[contour].clear();
for (int point = 0, numPoints = (int)c.size(); point < numPoints; ++point) {
const Point& p = c[point];
if (p.y >= box.min.y) {
bufferA[contour].push_back(p);
}
const Point& n = c[(point + 1) % numPoints];
float min_y = p.y < n.y ? p.y : n.y;
float max_y = p.y > n.y ? p.y : n.y;
if (min_y <= box.min.y && max_y >= box.min.y) {
bool horizontal = fabsf(n.y - p.y) < 0.0001f;
if (!horizontal) {
Point clip{ p.x + (n.x - p.x) * (box.min.y - p.y) / (n.y - p.y), box.min.y };
bufferA[contour].push_back(clip);
}
}
}
}
// Clip bottom
for (int contour = 0; contour < numContours; ++contour) {
const Contour& c = bufferA[contour];
bufferB[contour].clear();
for (int point = 0, numPoints = (int)c.size(); point < numPoints; ++point) {
const Point& p = c[point];
if (p.y <= box.max.y) {
bufferB[contour].push_back(p);
}
const Point& n = c[(point + 1) % numPoints];
float min_y = p.y < n.y ? p.y : n.y;
float max_y = p.y > n.y ? p.y : n.y;
if (min_y <= box.max.y && max_y >= box.max.y) {
bool horizontal = fabsf(n.y - p.y) < 0.0001f;
if (!horizontal) {
Point clip{ p.x + (n.x - p.x) * (box.max.y - p.y) / (n.y - p.y), box.max.y };
bufferB[contour].push_back(clip);
}
}
}
}
// Clean up polygon by removing empty contours
for (int i = bufferB.size() - 1; i >= 0; --i) {
if (bufferB[i].size() == 0) {
bufferB.erase(bufferB.begin() + i);
}
}
return bufferB;
}
struct PolygonTopmostY {
inline bool operator() (const RenderEdge& left, const RenderEdge& right) {
float lY = left.p0.y < left.p1.y ? left.p0.y : left.p1.y;
float rY = right.p0.y < right.p1.y ? right.p0.y : right.p1.y;
return lY < rY;
}
};
bool PolygonLeftmostXIntersection(const RenderEdge& left, const RenderEdge& right) {
return left.x < right.x;
}
unsigned char GetCoverage(const Polygon& poly, const Point& point) {
InitCoverageTable();
BoundingBox pixel {
Point { (float)((int)(point.x)), (float)((int)(point.y)) },
Point { (float)(int(point.x) + 1), (float)(int(point.y) + 1) }
};
Polygon clipped = ClipPolygon(poly, pixel);
uint64_t coverage = 0;
for (int contour = 0, numContours = (int)clipped.size(); contour < numContours; ++contour) {
const Contour& c = clipped[contour];
for (int point = 0, numPoints = (int)c.size(); point < numPoints; ++point) {
const Point& p = c[point];
const Point& n = c[(point + 1) % numPoints];
// Dont count if on the border of bottom
bool p_on_edge = fabsf(pixel.max.y - p.y) < 0.0001f;
bool n_on_edge = fabsf(pixel.max.y - n.y) < 0.0001f;
if (p_on_edge && n_on_edge) {
continue;
}
// Dont count if on the border of top
p_on_edge = fabsf(pixel.min.y - p.y) < 0.0001f;
n_on_edge = fabsf(pixel.min.y - n.y) < 0.0001f;
if (p_on_edge && n_on_edge) {
continue;
}
// Dont count if on the border of right
p_on_edge = fabsf(pixel.max.x - p.x) < 0.0001f;
n_on_edge = fabsf(pixel.max.x - n.x) < 0.0001f;
if (p_on_edge && n_on_edge) {
continue;
}
float p0x = (p.x - pixel.min.x) * 7.0f;
float p0y = (p.y - pixel.min.y) * 7.0f;
float p1x = (n.x - pixel.min.x) * 7.0f;
float p1y = (n.y - pixel.min.y) * 7.0f;
int p0_index = int(p0y) * 8 + int(p0x);
int p1_index = int(p1y) * 8 + int(p1x);
coverage ^= CoverageTable[p0_index][p1_index];
}
}
float alpha = float(std::bitset<64>(coverage).count()) / 64.0f;
return (unsigned char)(alpha * 255);
}
void DrawPolygon(int _x, int _y, const Polygon& poly, unsigned char r, unsigned char g, unsigned char b) {
BoundingBox bbox = GetBoundingBox(poly);
int s_x = bbox.min.x < 0.0f ? bbox.min.x - 0.5f : bbox.min.x + 0.5f;
int e_x = bbox.max.x < 0.0f ? bbox.max.x - 0.5f : bbox.max.x + 0.5f;
int s_y = bbox.min.y < 0.0f ? bbox.min.y - 0.5f : bbox.min.y + 0.5f;
int e_y = bbox.max.y < 0.0f ? bbox.max.y - 0.5f : bbox.max.y + 0.5f;
#if 0
for (int y = s_y; y <= e_y; ++y) {
for (int x = s_x; x < e_x; ++x) {
if (PointInPolygon(Point{ (float)x + 0.5f, (float)y + 0.5f }, poly)) {
DrawPixel(x + _x, y + _y, r, g, b);
}
}
}
#elif 0
std::vector<RenderEdge> edges;
for (int c = 0, cSize = poly.size(); c < cSize; ++c) {
const Contour& countour = poly[c];
int numPoints = countour.size();
for (int p = 0; p < numPoints; ++p) {
const Point& point = countour[p];
const Point& next = countour[(p + 1) % numPoints];
edges.push_back(RenderEdge{ point, next, 0.0f });
}
}
std::sort(edges.begin(), edges.end(), PolygonTopmostY());
std::list<RenderEdge> active;
unsigned int numEdges = edges.size();
unsigned int firstEdgeBelowScanline = 0;
for (int y = s_y; y <= e_y; ++y) {
// Sample from center of pixel
float sample_y = float(y) + 0.5f;
// Add any edges that start above the scanline
while (firstEdgeBelowScanline < numEdges) {
const RenderEdge& edge = edges[firstEdgeBelowScanline];
float min_y = edge.p0.y < edge.p1.y ? edge.p0.y : edge.p1.y;
if (min_y <= sample_y) { // Inclusive of topmost point
// firstEdgeBelowScanline was above the scanline
active.push_back(edges[firstEdgeBelowScanline]);
firstEdgeBelowScanline += 1;
}
else { // firstEdgeBelowScanline is below the scanline
break;
}
}
// Horizontal edges are not a problem, they are added above and removed immediateley below
// Remove any active edges that end above the scanline
for (std::list<RenderEdge>::iterator it = active.begin(); it != active.end();) {
float max_y = it->p0.y > it->p1.y ? it->p0.y : it->p1.y;
if (max_y <= sample_y) { // Exclusive of bottom most point
it = active.erase(it);
}
else { // Iterator crosses scanline, move to next one
it++;
}
}
// Find the x value of where each edge intersects the scanline
for (std::list<RenderEdge>::iterator it = active.begin(), end = active.end(); it != end; ++it) {
if (fabs(it->p0.x - it->p1.x) < 0.0001f) { // Vertical
it->x = it->p0.x;
}
else {
float m = (it->p1.y - it->p0.y) / (it->p1.x - it->p0.x);
float b = it->p0.y - m * it->p0.x;
it->x = (sample_y - b) / m;
}
}
// Sort edges by ascending x intersection order
active.sort(PolygonLeftmostXIntersection);
// Init winding for scanline
int winding = 0;
// Draw scanline
std::list<RenderEdge>::iterator it = active.begin();
std::list<RenderEdge>::iterator end = active.end();
for (int x = s_x; x <= e_x; ++x) {
float sample_x = float(x) + 0.5f;
// If any edges are on the left of the sample point
// update the running winding number and remove the edge
while (it != end && it->x < sample_x) {
if (it->p0.y <= sample_y && it->p1.y > sample_y) { // Crossing scanline top to bottom
winding -= 1;
}
else if (it->p1.y <= sample_y && it->p0.y > sample_y) { // Crossing scanline bottom up
winding += 1;
}
++it;
}
if (winding != 0) {
DrawPixel(_x + x, _y + y, r, g, b);
}
}
}
#elif 1
for (int y = s_y; y <= e_y; ++y) {
for (int x = s_x; x < e_x; ++x) {
unsigned char alpha = GetCoverage(poly, Point{ (float)x + 0.5f, (float)y + 0.5f });
if (alpha != 0) {
DrawPixel(x + _x, y + _y, r, g, b, alpha);
}
}
}
#endif
}
}
#pragma once
#include <vector>
namespace WindowsDotHDefinesPolygonWhichIsAPain {
struct Point {
float x;
float y;
};
struct RenderEdge {
Point p0;
Point p1;
float x;
};
struct BoundingBox {
Point min;
Point max;
};
typedef std::vector< Point > Contour;
typedef std::vector< Contour > Polygon;
void DrawPolygon(int _x, int _y, const Polygon& poly, unsigned char r, unsigned char g, unsigned char b);
BoundingBox GetBoundingBox(const Polygon& poly);
bool PointInPolygon(const Point& point, const Polygon& poly);
Polygon ClipPolygon(const Polygon& poly, const BoundingBox& box);
unsigned char GetCoverage(const Polygon& poly, const Point& point);
}
#pragma warning(disable : 28251)
#pragma warning(disable : 28159)
#define DPI_AWARE 0
#define WINDOW_TITLE L"Win32 Window"
#define WINDOW_CLASS L"FontDemo"
#pragma comment(lib, "Shcore.lib")
#pragma comment( linker, "/subsystem:console" )
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <windows.h>
#include <iostream>
#include <ShellScalingAPI.h>
#include "Polygon.h"
using namespace WindowsDotHDefinesPolygonWhichIsAPain;
#if _DEBUG
#include <crtdbg.h>
#endif
#undef min
#undef max
typedef int i32;
typedef float f32;
typedef unsigned int u32;
typedef unsigned char u8;
typedef unsigned short u16;
BITMAPINFO gFrameBufferInfo;
u8* gFrameBufferRGBA = 0;
u32 gFrameBufferWidth = 0;
u32 gFrameBufferHeight = 0;
u32 gDPIValue;
f32 gDPIScale;
void DrawPixel(i32 x, i32 y, u8 r, u8 g, u8 b);
void DrawPoint(i32 x, i32 y, u8 r, u8 g, u8 b);
void DrawLine(i32 x0, i32 y0, i32 x1, i32 y1, u8 r, u8 g, u8 b);
void DrawPixel(int x, int y, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
float Rs = float(r) / 255.0f;
float Gs = float(g) / 255.0f;
float Bs = float(b) / 255.0f;
float As = float(a) / 255.0f;
float inv = 1.0f - As;
float Rd = 125.0f / 255.0f;
float Gd = 125.0f / 255.0f;
float Bd = 125.0f / 255.0f;
DrawPixel(x, y,
(Rs * As + Rd * inv) * 255.0f,
(Gs * As + Gd * inv) * 255.0f,
(Bs * As + Bd * inv) * 255.0f
);
}
void DrawFrame() {
WindowsDotHDefinesPolygonWhichIsAPain::Polygon polygon;
polygon.resize(2);
polygon[0].resize(14);
polygon[1].resize(8);
polygon[0][0] = Point{ 475.17f, 10.000 + 10.0f };
polygon[0][1] = Point{ 475.17f, 79.792 + 10.0f };
polygon[0][2] = Point{ 320.48f, -2.500 + 10.0f };
polygon[0][3] = Point{ 198.60f, 33.958 + 10.0f };
polygon[0][4] = Point{ 112.67f, 135.52 + 10.0f };
polygon[0][5] = Point{ 82.458f, 286.04 + 10.0f };
polygon[0][6] = Point{ 110.06f, 436.04 + 10.0f };
polygon[0][7] = Point{ 192.88f, 539.69 + 10.0f };
polygon[0][8] = Point{ 316.31f, 575.62 + 10.0f };
polygon[0][9] = Point{ 405.38f, 554.27 + 10.0f };
polygon[0][10] = Point{ 468.92f, 499.58 + 10.0f };
polygon[0][11] = Point{ 468.92f, 773.54 + 10.0f };
polygon[0][12] = Point{ 562.15f, 773.54 + 10.0f };
polygon[0][13] = Point{ 562.15f, 10.000 + 10.0f };
polygon[1][0] = Point{ 178.81f, 286.04 + 10.0f };
polygon[1][1] = Point{ 223.60f, 127.19 + 10.0f };
polygon[1][2] = Point{ 329.33f, 74.583 + 10.0f };
polygon[1][3] = Point{ 433.50f, 124.58 + 10.0f };
polygon[1][4] = Point{ 476.73f, 278.23 + 10.0f };
polygon[1][5] = Point{ 432.98f, 444.90 + 10.0f };
polygon[1][6] = Point{ 325.17f, 498.02 + 10.0f };
polygon[1][7] = Point{ 220.48f, 446.98 + 10.0f };
DrawPolygon(0, 0, polygon, 255, 0, 0);
BoundingBox box{
Point{10, 300},
Point{700, 500}
};
auto clipped = ClipPolygon(polygon, box);
DrawPolygon(0, 0, clipped, 0, 255, 0);
box = BoundingBox {
Point{500, 100},
Point{550, 500}
};
clipped = ClipPolygon(polygon, box);
DrawPolygon(0, 0, clipped, 0, 0, 255);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow);
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
#include <bitset>
int main(int argc, const char** argv) {
#if _DEBUG
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
#endif
u16 a = 16;
u16 b = 987;
u32 c = u32(a) << 16 | u32(b);
u16 d = (u16)(c >> 16);
u16 e = (u16)(c & ((1 << 17) - 1));
std::cout << "a: " << a << "\t\t, " << std::bitset<32>(a) << "\n";
std::cout << "b: " << b << "\t\t, " << std::bitset<32>(b) << "\n";
std::cout << "c: " << c << "\t, " << std::bitset<32>(c) << "\n";
std::cout << "d: " << d << "\t\t, " << std::bitset<32>(d) << "\n";
std::cout << "e: " << e << "\t\t, " << std::bitset<32>(e) << "\n";
WinMain(GetModuleHandle(NULL), NULL, GetCommandLineA(), SW_SHOWDEFAULT);
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
HRESULT hr = SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = 0;
wndclass.lpszClassName = WINDOW_CLASS;
RegisterClassEx(&wndclass);
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int clientWidth = 800;
int clientHeight = 600;
gDPIValue = GetDpiForSystem();
gDPIScale = (float)gDPIValue / 96.0f;
std::cout << "DPI: " << gDPIValue << ", scale: " << gDPIScale << "\n";
clientWidth = (int)((float)clientWidth * gDPIScale);
clientHeight = (int)((float)clientHeight * gDPIScale);
RECT windowRect;
SetRect(&windowRect, (screenWidth / 2) - (clientWidth / 2), (screenHeight / 2) - (clientHeight / 2), (screenWidth / 2) + (clientWidth / 2), (screenHeight / 2) + (clientHeight / 2));
DWORD style = (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU);
AdjustWindowRectEx(&windowRect, style, FALSE, 0);
HWND hwnd = CreateWindowEx(0, wndclass.lpszClassName, WINDOW_TITLE, style, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, NULL, NULL, hInstance, szCmdLine);
gFrameBufferInfo.bmiHeader.biSize = sizeof(gFrameBufferInfo.bmiHeader);
gFrameBufferInfo.bmiHeader.biWidth = clientWidth;
gFrameBufferInfo.bmiHeader.biHeight = -clientHeight;
gFrameBufferInfo.bmiHeader.biPlanes = 1;
gFrameBufferInfo.bmiHeader.biBitCount = 32;
gFrameBufferInfo.bmiHeader.biCompression = BI_RGB;
gFrameBufferWidth = clientWidth;
gFrameBufferHeight = clientHeight;
int bitmapMemorySize = (gFrameBufferWidth * gFrameBufferHeight) * 4;
gFrameBufferRGBA = (unsigned char*)VirtualAlloc(0, bitmapMemorySize, MEM_COMMIT, PAGE_READWRITE);
memset(gFrameBufferRGBA, (125) | (125 << 8) | (125 << 16) | (255 << 24), bitmapMemorySize);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
VirtualFree(gFrameBufferRGBA, 0, MEM_RELEASE);
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
switch (iMsg) {
case WM_CREATE:
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
{
i32 bitmapMemorySize = (gFrameBufferWidth * gFrameBufferHeight) * 4;
memset(gFrameBufferRGBA, (125) | (125 << 8) | (125 << 16) | (255 << 24), bitmapMemorySize);
DrawFrame();
PAINTSTRUCT ps;
RECT clientRect;
HDC hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &clientRect);
i32 clientWidth = clientRect.right - clientRect.left;
i32 clientHeight = clientRect.bottom - clientRect.top;
StretchDIBits(hdc,
0, 0, clientWidth, clientHeight,
0, 0, gFrameBufferWidth, gFrameBufferHeight,
gFrameBufferRGBA, &gFrameBufferInfo,
DIB_RGB_COLORS, SRCCOPY);
EndPaint(hwnd, &ps);
return 0;
}
break;
case WM_ERASEBKGND:
return 0;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
void DrawPixel(i32 x, i32 y, u8 r, u8 g, u8 b) {
if (x < 0 || x >= gFrameBufferWidth || y < 0 || y >= gFrameBufferHeight) {
return;
}
i32 pixel = (y * gFrameBufferWidth + x) * 4;
gFrameBufferRGBA[pixel + 0] = b;
gFrameBufferRGBA[pixel + 1] = g;
gFrameBufferRGBA[pixel + 2] = r;
gFrameBufferRGBA[pixel + 3] = 255;
}
void DrawPoint(i32 x, i32 y, u8 r, u8 g, u8 b) {
DrawPixel(x + 0, y, r, g, b);
DrawPixel(x - 1, y, r, g, b);
DrawPixel(x + 1, y, r, g, b);
DrawPixel(x + 2, y, r, g, b);
DrawPixel(x + 0, y - 1, r, g, b);
DrawPixel(x - 1, y - 1, r, g, b);
DrawPixel(x + 1, y - 1, r, g, b);
DrawPixel(x + 2, y - 1, r, g, b);
DrawPixel(x + 0, y + 1, r, g, b);
DrawPixel(x + 1, y + 1, r, g, b);
DrawPixel(x + 0, y - 2, r, g, b);
DrawPixel(x + 1, y - 2, r, g, b);
}
void DrawLine(i32 x0, i32 y0, i32 x1, i32 y1, u8 r, u8 g, u8 b) {
i32 dx = abs(x1 - x0);
i32 dy = abs(y1 - y0);
i32 xStep = x0 < x1 ? 1 : -1;
i32 yStep = y0 < y1 ? 1 : -1;
i32 error = 0;
if (dx > dy) {
i32 m = 2 * dy;
i32 scale = 2 * dx;
for (i32 x = x0, y = y0; x != x1 + xStep; x += xStep) {
DrawPixel(x, y, r, g, b);
error += m;
if (error >= dx) {
y += yStep;
error -= scale;
}
}
}
else {
i32 m = 2 * dx;
i32 scale = 2 * dy;
for (i32 y = y0, x = x0; y != y1 + yStep; y += yStep) {
DrawPixel(x, y, r, g, b);
error += m;
if (error >= dy) {
x += xStep;
error -= scale;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment