Created
October 14, 2021 02:19
-
-
Save gszauer/fd16f62338decb053c2a7343eed128c6 to your computer and use it in GitHub Desktop.
PolygonRenderer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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