671 lines
22 KiB
C++
671 lines
22 KiB
C++
//
|
|
// Created by Bobby Lucero on 4/20/24.
|
|
//
|
|
|
|
#include "Graphics.h"
|
|
#include "PycronImage.h"
|
|
#include "Font.h"
|
|
#include "../Utilities.h"
|
|
#include "../StateManager.h"
|
|
|
|
#include <fstream>
|
|
#include <raymath.h>
|
|
|
|
|
|
|
|
|
|
Graphics::Graphics(int screenWidth, int screenHeight, int startupScale) : m_screenWidth(screenWidth), m_screenHeight(screenHeight){
|
|
|
|
m_NormalFont = new PixelFont(5, 7, "resources/fonts/main.font");
|
|
m_currentFont = m_NormalFont;
|
|
|
|
m_startupScreenWidth = screenWidth * startupScale;
|
|
m_startupScreenHeight = screenHeight * startupScale;
|
|
m_windowWidth = m_startupScreenWidth;
|
|
m_windowHeight = m_startupScreenHeight;
|
|
|
|
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
|
|
InitWindow(m_startupScreenWidth, m_startupScreenHeight, "test");
|
|
SetTargetFPS(60);
|
|
|
|
m_virtualScreen = LoadRenderTexture(screenWidth, screenHeight);
|
|
m_origin = {0,0};
|
|
m_virtualScreenLocalBounds = {0.0f, 0.0f, (float)m_virtualScreen.texture.width, (float)m_virtualScreen.texture.height };
|
|
m_virtualScreenWindowBounds = {0.0f, 0.0f, (float)m_windowWidth, (float)m_windowHeight};
|
|
|
|
m_virtualScreenImageBuffer = GenImageColor(m_screenWidth, m_screenHeight, BLACK);
|
|
m_virtualScreenColorBuffer = {};
|
|
m_virtualScreenColorBuffer.resize(screenWidth * screenHeight);
|
|
|
|
calculateScreenPositionInWindow();
|
|
|
|
}
|
|
|
|
Graphics::~Graphics() {
|
|
delete m_NormalFont;
|
|
m_NormalFont = nullptr;
|
|
m_currentFont = nullptr;
|
|
}
|
|
|
|
void Graphics::draw(StateManager* stateManager) {
|
|
m_windowShouldClose = WindowShouldClose();
|
|
|
|
if (IsWindowResized()) {
|
|
m_windowWidth = GetScreenWidth();
|
|
m_windowHeight = GetScreenHeight();
|
|
calculateScreenPositionInWindow();
|
|
}
|
|
|
|
stateManager->Draw();
|
|
copyBufferToGPU();
|
|
renderVirtualScreen();
|
|
}
|
|
|
|
void Graphics::copyBufferToGPU() {
|
|
Color* pixel_data = LoadImageColors(m_virtualScreenImageBuffer);
|
|
for (int i = 0; i < m_screenWidth * m_screenHeight; ++i) {
|
|
uint32_t rgb = m_paletteByID[m_virtualScreenColorBuffer[i]];
|
|
uint8_t r = rgb >> 16 & 0xFF;
|
|
uint8_t g = rgb >> 8 & 0xFF;
|
|
uint8_t b = rgb & 0xFF;
|
|
|
|
double dR = (double)r * 0.2126;
|
|
double dG = (double)g * 0.7152;
|
|
double dB = (double)b * 0.0722;
|
|
|
|
int gray = (int)(dR + dG + dB);
|
|
double naiveGray = (r + g + b) / 3.0;
|
|
//gray = (int)naiveGray;
|
|
unsigned int out = 255;
|
|
out |= gray << 8;
|
|
out |= gray << 16;
|
|
out |= gray << 24;
|
|
pixel_data[i] = GetColor(rgb);
|
|
}
|
|
UpdateTexture(m_virtualScreen.texture, pixel_data);
|
|
UnloadImageColors(pixel_data);
|
|
}
|
|
|
|
void Graphics::renderVirtualScreen() {
|
|
BeginDrawing();
|
|
ClearBackground(BLACK);
|
|
DrawTexturePro(m_virtualScreen.texture, m_virtualScreenLocalBounds, m_virtualScreenWindowBounds, m_origin, 0.0f, WHITE);
|
|
DrawText(std::to_string(GetFPS()).c_str(), 10, 10, 30, YELLOW);
|
|
|
|
EndDrawing();
|
|
}
|
|
|
|
void Graphics::loadPalette(std::string path) {
|
|
std::ifstream paletteFile(path);
|
|
std::string line;
|
|
|
|
if(paletteFile.is_open()){
|
|
int idx = 0;
|
|
while(getline(paletteFile, line)){
|
|
int color = stoi(line, nullptr, 16);
|
|
//Palette.push_back();
|
|
color = color << 8 | 0xFF;
|
|
m_paletteByID.insert({idx, color});
|
|
idx++;
|
|
}
|
|
paletteFile.close();
|
|
}
|
|
}
|
|
|
|
void Graphics::calculateScreenPositionInWindow() {
|
|
float virtualAspectRatio = (float)m_screenWidth / (float)m_screenHeight;
|
|
float windowAspectRatio = (float)m_windowWidth / (float)m_windowHeight;
|
|
|
|
if(windowAspectRatio > virtualAspectRatio) {
|
|
m_virtualScreenWindowBounds.height = (float)m_windowHeight;
|
|
m_virtualScreenWindowBounds.width = m_virtualScreenWindowBounds.height * virtualAspectRatio;
|
|
m_origin.x = -(m_windowWidth / 2.0f - (m_virtualScreenWindowBounds.width / 2.0f));
|
|
m_origin.y = 0;
|
|
}else {
|
|
m_virtualScreenWindowBounds.width = (float)m_windowWidth;
|
|
m_virtualScreenWindowBounds.height = m_virtualScreenWindowBounds.width / virtualAspectRatio;
|
|
m_origin.x = 0;
|
|
m_origin.y = -(m_windowHeight / 2.0f - (m_virtualScreenWindowBounds.height / 2.0f));
|
|
}
|
|
}
|
|
|
|
int Graphics::rgbToID(int r, int g, int b) {
|
|
r = Clamp(r, 0, 255);
|
|
g = Clamp(g, 0, 255);
|
|
b = Clamp(b, 0, 255);
|
|
uint8_t idx = 0;
|
|
Color c = GetColor(m_paletteByID[idx]);
|
|
double minDist = std::pow(r - c.r, 2) +
|
|
std::pow(g - c.g, 2) +
|
|
std::pow(b - c.b, 2);
|
|
|
|
for(const auto& paletteColor : m_paletteByID){
|
|
c = GetColor(paletteColor.second);
|
|
double dist = std::pow(r - c.r, 2) +
|
|
std::pow(g - c.g, 2) +
|
|
std::pow(b - c.b, 2);
|
|
if(dist < minDist){
|
|
minDist = dist;
|
|
idx = paletteColor.first;
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
int Graphics::mouseX() {
|
|
float x = GetMouseX();
|
|
float adjX = x + m_origin.x;
|
|
return (int)(adjX / m_virtualScreenWindowBounds.width * m_screenWidth);
|
|
}
|
|
|
|
int Graphics::mouseY() {
|
|
float y = GetMouseY();
|
|
float adjY = y + m_origin.y;
|
|
return (int)(adjY / m_virtualScreenWindowBounds.height * m_screenHeight);
|
|
}
|
|
|
|
void Graphics::toggleFullScreen() {
|
|
if (IsWindowFullscreen()) {
|
|
ToggleFullscreen();
|
|
SetWindowSize(m_startupScreenWidth, m_startupScreenHeight);
|
|
m_windowWidth = m_startupScreenWidth;
|
|
m_windowHeight = m_startupScreenHeight;
|
|
} else {
|
|
int monitor = GetCurrentMonitor();
|
|
m_windowWidth = GetMonitorWidth(monitor);
|
|
m_windowHeight = GetMonitorHeight(monitor);
|
|
SetWindowSize(m_windowWidth, m_windowHeight);
|
|
ToggleFullscreen();
|
|
|
|
}
|
|
calculateScreenPositionInWindow();
|
|
}
|
|
|
|
void Graphics::bindMethods(pkpy::VM *vm) {
|
|
vm->bind(vm->builtins, "clear(color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){
|
|
int index = pkpy::py_cast<int>(vm, args[0]);
|
|
Clear(index);
|
|
return vm->None;
|
|
});
|
|
|
|
vm->bind(vm->builtins, "pixel(x: int, y: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){
|
|
float x = pkpy::py_cast<float>(vm, args[0]);
|
|
float y = pkpy::py_cast<float>(vm, args[1]);
|
|
float paletteIndex = pkpy::py_cast<float>(vm, args[2]);
|
|
this->Pixel(x, y, paletteIndex);
|
|
return vm->None;
|
|
});
|
|
|
|
vm->bind(vm->builtins, "getPixel(x: int, y: int) -> int", [this](pkpy::VM* vm, pkpy::ArgsView args){
|
|
float x = pkpy::py_cast<float>(vm, args[0]);
|
|
float y = pkpy::py_cast<float>(vm, args[1]);
|
|
|
|
return pkpy::py_var(vm, this->GetPixel(x, y));
|
|
});
|
|
|
|
vm->bind(vm->builtins, "circle(x: int, y: int, radius: float, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){
|
|
float x = pkpy::py_cast<float>(vm, args[0]);
|
|
float y = pkpy::py_cast<float>(vm, args[1]);
|
|
float radius = pkpy::py_cast<float>(vm, args[2]);
|
|
float paletteIndex = pkpy::py_cast<float>(vm, args[3]);
|
|
this->Circle(x, y, radius, paletteIndex);
|
|
return vm->None;
|
|
});
|
|
|
|
vm->bind(vm->builtins, "rect(x: int, y: int, width: int, height: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){
|
|
float x = pkpy::py_cast<float>(vm, args[0]);
|
|
float y = pkpy::py_cast<float>(vm, args[1]);
|
|
float width = pkpy::py_cast<float>(vm, args[2]);
|
|
float height = pkpy::py_cast<float>(vm, args[3]);
|
|
float paletteIndex = pkpy::py_cast<float>(vm, args[4]);
|
|
this->Rect(x, y, width, height, paletteIndex);
|
|
return vm->None;
|
|
});
|
|
|
|
vm->bind(vm->builtins, "rectBorder(x: int, y: int, width: int, height: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){
|
|
float x = pkpy::py_cast<float>(vm, args[0]);
|
|
float y = pkpy::py_cast<float>(vm, args[1]);
|
|
float width = pkpy::py_cast<float>(vm, args[2]);
|
|
float height = pkpy::py_cast<float>(vm, args[3]);
|
|
float paletteIndex = pkpy::py_cast<float>(vm, args[4]);
|
|
this->RectBorder(x, y, width, height, paletteIndex);
|
|
return vm->None;
|
|
});
|
|
|
|
vm->bind(vm->builtins, "triangle(x1: int, y1: int, x2: int, y2: int, x3: int, y3: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){
|
|
float x1 = pkpy::py_cast<float>(vm, args[0]);
|
|
float y1 = pkpy::py_cast<float>(vm, args[1]);
|
|
float x2 = pkpy::py_cast<float>(vm, args[2]);
|
|
float y2 = pkpy::py_cast<float>(vm, args[3]);
|
|
float x3 = pkpy::py_cast<float>(vm, args[4]);
|
|
float y3 = pkpy::py_cast<float>(vm, args[5]);
|
|
float paletteIndex = pkpy::py_cast<float>(vm, args[6]);
|
|
this->Triangle(x1, y1, x2, y2, x3, y3, paletteIndex);
|
|
return vm->None;
|
|
});
|
|
|
|
vm->bind(vm->builtins, "line(x1: int, y1: int, x2: int, y2: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){
|
|
float x1 = pkpy::py_cast<float>(vm, args[0]);
|
|
float y1 = pkpy::py_cast<float>(vm, args[1]);
|
|
float x2 = pkpy::py_cast<float>(vm, args[2]);
|
|
float y2 = pkpy::py_cast<float>(vm, args[3]);
|
|
float paletteIndex = pkpy::py_cast<float>(vm, args[4]);
|
|
this->Line(x1, y1, x2, y2, paletteIndex);
|
|
return vm->None;
|
|
});
|
|
|
|
vm->bind(vm->builtins, "text(t: string, x: int, y: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){
|
|
pkpy::PyObject* func_str = vm->builtins->attr("str");
|
|
pkpy::PyObject* result = vm->call(func_str, args[0]);
|
|
std::string s = pkpy::py_cast<pkpy::Str&>(vm, result).str();
|
|
|
|
float x = pkpy::py_cast<float>(vm, args[1]);
|
|
float y = pkpy::py_cast<float>(vm, args[2]);
|
|
float paletteIndex = pkpy::py_cast<float>(vm, args[3]);
|
|
this->Text(s, x, y, paletteIndex);
|
|
return vm->None;
|
|
});
|
|
}
|
|
|
|
void Graphics::Clear(int paletteIndex) {
|
|
for (int y = 0; y < m_screenHeight; ++y) {
|
|
for (int x = 0; x < m_screenWidth; ++x) {
|
|
Pixel(x, y, paletteIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Graphics::Pixel(int x, int y, int paletteIndex) {
|
|
paletteIndex = Clamp(paletteIndex, 0, m_paletteByID.size() - 1);
|
|
if(x < 0 || y < 0 || x >= m_screenWidth || y >= m_screenHeight) return;
|
|
m_virtualScreenColorBuffer[y * m_screenWidth + x] = paletteIndex;
|
|
}
|
|
|
|
void Graphics::Line(int x0, int y0, int x1, int y1, int paletteIndex) {
|
|
int dx = x1 - x0; // Change in x
|
|
int dy = y1 - y0; // Change in y
|
|
int abs_dx = std::abs(dx);
|
|
int abs_dy = std::abs(dy);
|
|
|
|
int sx = (dx > 0) ? 1 : -1; // Step direction in x
|
|
int sy = (dy > 0) ? 1 : -1; // Step direction in y
|
|
|
|
// Choose the primary axis for iteration
|
|
if (abs_dx > abs_dy) {
|
|
int err = abs_dx / 2; // Error value
|
|
for (int i = 0; i <= abs_dx; i++) {
|
|
Pixel(x0, y0, paletteIndex); // Plot the pixel
|
|
err -= abs_dy; // Update error term
|
|
if (err < 0) {
|
|
y0 += sy; // Move in y direction
|
|
err += abs_dx; // Update error term
|
|
}
|
|
x0 += sx; // Always move in x direction
|
|
}
|
|
} else {
|
|
int err = abs_dy / 2; // Error value
|
|
for (int i = 0; i <= abs_dy; i++) {
|
|
Pixel(x0, y0, paletteIndex); // Plot the pixel
|
|
err -= abs_dx; // Update error term
|
|
if (err < 0) {
|
|
x0 += sx; // Move in x direction
|
|
err += abs_dy; // Update error term
|
|
}
|
|
y0 += sy; // Always move in y direction
|
|
}
|
|
}
|
|
}
|
|
|
|
void Graphics::Circle(int x, int y, int radius, int paletteIndex) {
|
|
Ellipse(x, y, radius, radius, paletteIndex);
|
|
}
|
|
|
|
void Graphics::Ellipse(int x, int y, int w, int h, int paletteIndex){
|
|
|
|
int x0 = x - w;
|
|
int y0 = y - h;
|
|
int x1 = x + w;
|
|
int y1 = y + h;
|
|
|
|
if(x0 > x1 || y0 > y1)
|
|
return;
|
|
|
|
int a = abs(x1 - x0), b = abs(y1 - y0), b1 = b & 1;
|
|
int dx = 4 * (1 - a) * b * b, dy = 4 * (b1 + 1) * a * a;
|
|
int err = dx + dy + b1 * a * a, e2;
|
|
|
|
if (x0 > x1) { x0 = x1; x1 += a; }
|
|
if (y0 > y1) y0 = y1;
|
|
y0 += (b + 1) / 2; y1 = y0 - b1;
|
|
a *= 8 * a; b1 = 8 * b * b;
|
|
|
|
int prevY = y0;
|
|
do
|
|
{
|
|
Pixel( x1, y0, paletteIndex);
|
|
Pixel( x0, y0, paletteIndex);
|
|
Pixel( x0, y1, paletteIndex);
|
|
Pixel( x1, y1, paletteIndex);
|
|
if(y0 != prevY && (y0 - y) != h){
|
|
h_line(x1, y0, x - (x1 - x) + 1, paletteIndex);
|
|
h_line(x1, y - (y0 - y), x - (x1 - x) + 1, paletteIndex);
|
|
}
|
|
prevY = y0;
|
|
e2 = 2 * err;
|
|
if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */
|
|
if (e2 >= dx || 2 * err > dy) { x0++; x1--; err += dx += b1; } /* x step */
|
|
} while (x0 <= x1);
|
|
|
|
while (y0-y1 < b)
|
|
{ /* too early stop of flat ellipses a=1 */
|
|
Pixel( x0 - 1, y0, paletteIndex); /* -> finish tip of ellipse */
|
|
Pixel( x1 + 1, y0++, paletteIndex);
|
|
Pixel( x0 - 1, y1, paletteIndex);
|
|
Pixel( x1 + 1, y1--, paletteIndex);
|
|
}
|
|
|
|
|
|
h_line(x - w, y, x + w, paletteIndex);
|
|
}
|
|
|
|
void Graphics::EllipseBorder(int x, int y, int w, int h, int paletteIndex){
|
|
|
|
int x0 = x - w;
|
|
int y0 = y - h;
|
|
int x1 = x + w;
|
|
int y1 = y + h;
|
|
|
|
if(x0 > x1 || y0 > y1)
|
|
return;
|
|
|
|
int a = abs(x1 - x0), b = abs(y1 - y0), b1 = b & 1;
|
|
int dx = 4 * (1 - a) * b * b, dy = 4 * (b1 + 1) * a * a;
|
|
int err = dx + dy + b1 * a * a, e2;
|
|
|
|
if (x0 > x1) { x0 = x1; x1 += a; }
|
|
if (y0 > y1) y0 = y1;
|
|
y0 += (b + 1) / 2; y1 = y0 - b1;
|
|
a *= 8 * a; b1 = 8 * b * b;
|
|
|
|
do
|
|
{
|
|
Pixel( x1, y0, paletteIndex);
|
|
Pixel( x0, y0, paletteIndex);
|
|
Pixel( x0, y1, paletteIndex);
|
|
Pixel( x1, y1, paletteIndex);
|
|
|
|
e2 = 2 * err;
|
|
if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */
|
|
if (e2 >= dx || 2 * err > dy) { x0++; x1--; err += dx += b1; } /* x step */
|
|
} while (x0 <= x1);
|
|
|
|
while (y0-y1 < b)
|
|
{ /* too early stop of flat ellipses a=1 */
|
|
Pixel( x0 - 1, y0, paletteIndex); /* -> finish tip of ellipse */
|
|
Pixel( x1 + 1, y0++, paletteIndex);
|
|
Pixel( x0 - 1, y1, paletteIndex);
|
|
Pixel( x1 + 1, y1--, paletteIndex);
|
|
}
|
|
|
|
}
|
|
|
|
void Graphics::Rect(int x, int y, int width, int height, int paletteIndex) {
|
|
for (int i = 0; i < height; ++i) {
|
|
h_line(x, y + i, x + width - 1, paletteIndex);
|
|
}
|
|
}
|
|
|
|
void Graphics::RectBorder(int x, int y, int width, int height, int paletteIndex) {
|
|
h_line(x, y, x + width - 1, paletteIndex);
|
|
h_line(x, y + height - 1, x + width - 1, paletteIndex);
|
|
v_line(x, y + 1, y + height - 2, paletteIndex);
|
|
v_line(x + width - 1, y + 1, y + height - 2, paletteIndex);
|
|
}
|
|
|
|
void Graphics::Text(const std::string& s, int x, int y, int paletteIndex) {
|
|
int currentX = 0;
|
|
int currentY = y;
|
|
|
|
for (int i = 0; i < s.size(); ++i) {
|
|
char c = s[i];
|
|
// Handle new lines
|
|
if(c != '\n') {
|
|
std::string bitData = m_currentFont->GetCharData((int) c);
|
|
for (int j = 0; j < bitData.size(); ++j) {
|
|
if (bitData[j] == '1'){
|
|
Pixel(x + (j % m_currentFont->GetWidth()) + ((m_currentFont->GetWidth() + 1) * currentX),currentY + (j / m_currentFont->GetWidth()), paletteIndex);
|
|
}
|
|
|
|
}
|
|
currentX++;
|
|
}else{
|
|
currentX = 0;
|
|
currentY += m_currentFont->GetHeight() + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Graphics::Char(char c, int x, int y, int paletteIndex) {
|
|
std::string bitData = m_currentFont->GetCharData((int)c);
|
|
for (int j = 0; j < bitData.size(); ++j) {
|
|
if(bitData[j] == '1')
|
|
Pixel(x + (j % m_currentFont->GetWidth()), y + (j / m_currentFont->GetWidth()), paletteIndex);
|
|
|
|
}
|
|
}
|
|
|
|
void Graphics::updateVMVars(pkpy::VM* vm) {
|
|
vm->builtins->attr().set("mouseX", pkpy::py_var(vm, mouseX()));
|
|
vm->builtins->attr().set("mouseY", pkpy::py_var(vm, mouseY()));
|
|
vm->builtins->attr().set("width", pkpy::py_var(vm, m_screenWidth));
|
|
vm->builtins->attr().set("height", pkpy::py_var(vm, m_screenHeight));
|
|
}
|
|
|
|
void Graphics::h_line(int x1, int y, int x2, int paletteIndex) {
|
|
if(y < 0 || y >= m_screenHeight) return;
|
|
int startX = x1;
|
|
int endX = x2;
|
|
if(x1 > x2){
|
|
startX = x2;
|
|
endX = x1;
|
|
}
|
|
for (int i = startX; i <= endX; ++i) {
|
|
Pixel(i, y, paletteIndex);
|
|
}
|
|
}
|
|
|
|
void Graphics::v_line(int x, int y1, int y2, int paletteIndex) {
|
|
if(x < 0 || x >= m_screenWidth) return;
|
|
int startY = y1;
|
|
int endY = y2;
|
|
if(y1 > y2){
|
|
startY = y2;
|
|
endY = y1;
|
|
}
|
|
for (int i = startY; i <= endY; ++i) {
|
|
Pixel(x, i, paletteIndex);
|
|
}
|
|
}
|
|
|
|
int Graphics::GetPixel(int x, int y) {
|
|
if(x < 0 || y < 0 || x >= m_screenWidth || y >= m_screenHeight) return 0;
|
|
|
|
return m_virtualScreenColorBuffer[y * m_screenWidth + x];
|
|
}
|
|
|
|
// https://github.com/OneLoneCoder/olcPixelGameEngine/blob/master/olcPixelGameEngine.h#L2456
|
|
void Graphics::Triangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, int paletteIndex)
|
|
{
|
|
auto drawline = [&](int sx, int ex, int ny) { for (int i = sx; i <= ex; i++) Pixel(i, ny, paletteIndex); };
|
|
|
|
int t1x, t2x, y, minx, maxx, t1xp, t2xp;
|
|
bool changed1 = false;
|
|
bool changed2 = false;
|
|
int signx1, signx2, dx1, dy1, dx2, dy2;
|
|
int e1, e2;
|
|
// Sort vertices
|
|
if (y1 > y2) { std::swap(y1, y2); std::swap(x1, x2); }
|
|
if (y1 > y3) { std::swap(y1, y3); std::swap(x1, x3); }
|
|
if (y2 > y3) { std::swap(y2, y3); std::swap(x2, x3); }
|
|
|
|
t1x = t2x = x1; y = y1; // Starting points
|
|
dx1 = (int)(x2 - x1);
|
|
if (dx1 < 0) { dx1 = -dx1; signx1 = -1; }
|
|
else signx1 = 1;
|
|
dy1 = (int)(y2 - y1);
|
|
|
|
dx2 = (int)(x3 - x1);
|
|
if (dx2 < 0) { dx2 = -dx2; signx2 = -1; }
|
|
else signx2 = 1;
|
|
dy2 = (int)(y3 - y1);
|
|
|
|
if (dy1 > dx1) { std::swap(dx1, dy1); changed1 = true; }
|
|
if (dy2 > dx2) { std::swap(dy2, dx2); changed2 = true; }
|
|
|
|
e2 = (int)(dx2 >> 1);
|
|
// Flat top, just process the second half
|
|
if (y1 == y2) goto next;
|
|
e1 = (int)(dx1 >> 1);
|
|
|
|
for (int i = 0; i < dx1;) {
|
|
t1xp = 0; t2xp = 0;
|
|
if (t1x < t2x) { minx = t1x; maxx = t2x; }
|
|
else { minx = t2x; maxx = t1x; }
|
|
// process first line until y value is about to change
|
|
while (i < dx1) {
|
|
i++;
|
|
e1 += dy1;
|
|
while (e1 >= dx1) {
|
|
e1 -= dx1;
|
|
if (changed1) t1xp = signx1;//t1x += signx1;
|
|
else goto next1;
|
|
}
|
|
if (changed1) break;
|
|
else t1x += signx1;
|
|
}
|
|
// Move line
|
|
next1:
|
|
// process second line until y value is about to change
|
|
while (1) {
|
|
e2 += dy2;
|
|
while (e2 >= dx2) {
|
|
e2 -= dx2;
|
|
if (changed2) t2xp = signx2;//t2x += signx2;
|
|
else goto next2;
|
|
}
|
|
if (changed2) break;
|
|
else t2x += signx2;
|
|
}
|
|
next2:
|
|
if (minx > t1x) minx = t1x;
|
|
if (minx > t2x) minx = t2x;
|
|
if (maxx < t1x) maxx = t1x;
|
|
if (maxx < t2x) maxx = t2x;
|
|
drawline(minx, maxx, y); // Draw line from min to max points found on the y
|
|
// Now increase y
|
|
if (!changed1) t1x += signx1;
|
|
t1x += t1xp;
|
|
if (!changed2) t2x += signx2;
|
|
t2x += t2xp;
|
|
y += 1;
|
|
if (y == y2) break;
|
|
}
|
|
next:
|
|
// Second half
|
|
dx1 = (int)(x3 - x2); if (dx1 < 0) { dx1 = -dx1; signx1 = -1; }
|
|
else signx1 = 1;
|
|
dy1 = (int)(y3 - y2);
|
|
t1x = x2;
|
|
|
|
if (dy1 > dx1) { // swap values
|
|
std::swap(dy1, dx1);
|
|
changed1 = true;
|
|
}
|
|
else changed1 = false;
|
|
|
|
e1 = (int)(dx1 >> 1);
|
|
|
|
for (int i = 0; i <= dx1; i++) {
|
|
t1xp = 0; t2xp = 0;
|
|
if (t1x < t2x) { minx = t1x; maxx = t2x; }
|
|
else { minx = t2x; maxx = t1x; }
|
|
// process first line until y value is about to change
|
|
while (i < dx1) {
|
|
e1 += dy1;
|
|
while (e1 >= dx1) {
|
|
e1 -= dx1;
|
|
if (changed1) { t1xp = signx1; break; }//t1x += signx1;
|
|
else goto next3;
|
|
}
|
|
if (changed1) break;
|
|
else t1x += signx1;
|
|
if (i < dx1) i++;
|
|
}
|
|
next3:
|
|
// process second line until y value is about to change
|
|
while (t2x != x3) {
|
|
e2 += dy2;
|
|
while (e2 >= dx2) {
|
|
e2 -= dx2;
|
|
if (changed2) t2xp = signx2;
|
|
else goto next4;
|
|
}
|
|
if (changed2) break;
|
|
else t2x += signx2;
|
|
}
|
|
next4:
|
|
|
|
if (minx > t1x) minx = t1x;
|
|
if (minx > t2x) minx = t2x;
|
|
if (maxx < t1x) maxx = t1x;
|
|
if (maxx < t2x) maxx = t2x;
|
|
drawline(minx, maxx, y);
|
|
if (!changed1) t1x += signx1;
|
|
t1x += t1xp;
|
|
if (!changed2) t2x += signx2;
|
|
t2x += t2xp;
|
|
y += 1;
|
|
if (y > y3) return;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
PycronImage* Graphics::loadImage(std::string path) {
|
|
Image img = LoadImage(path.c_str());
|
|
ImageRotateCW(&img);
|
|
ImageFlipHorizontal(&img);
|
|
if(img.width == 0 && img.height == 0) std::cerr << "Image at path " << path << " is empty\n";
|
|
Color* colors = LoadImageColors(img);
|
|
auto* idImg = new PycronImage(img.width, img.height);
|
|
for (int i = 0; i < idImg->data.size(); ++i) {
|
|
if(colors[i].a == 0){
|
|
idImg->data[i] = -1;
|
|
}else{
|
|
idImg->data[i] = (int8_t)rgbToID(colors[i].r, colors[i].g, colors[i].b);
|
|
}
|
|
}
|
|
return idImg;
|
|
}
|
|
|
|
void Graphics::Img(PycronImage* img, int x, int y) {
|
|
for (int i = 0; i < img->data.size(); ++i) {
|
|
if(img->data[i] == -1) continue;
|
|
Pixel(x + i / img->width, y + i % img->width, img->data[i]);
|
|
}
|
|
}
|
|
|
|
int Graphics::GetCurrentFontWidth(){
|
|
return m_currentFont->GetWidth();
|
|
}
|
|
int Graphics::GetCurrentFontHeight(){
|
|
return m_currentFont->GetHeight();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|