#include "DrawContext.h" #include "FrameBuffer.h" #include "DepthBuffer.h" #include "Rasterizer.h" #include "TriangleRasterizer.h" #include "Display.h" #include namespace Core { DrawContext::DrawContext(int32_t width, int32_t height) { frameBuffer = new Core::FrameBuffer(width, height); depthBuffer = new Core::DepthBuffer(width, height); rasterizer = new Rasterizer::Rasterizer(frameBuffer, depthBuffer); triangleRasterizer = new Rasterizer::TriangleRasterizer(frameBuffer, depthBuffer); } DrawContext::~DrawContext() { delete triangleRasterizer; delete rasterizer; delete depthBuffer; delete frameBuffer; } int32_t DrawContext::get_width() const { return frameBuffer->get_width(); } int32_t DrawContext::get_height() const { return frameBuffer->get_height(); } void DrawContext::clear(const RenderData::Color& color) { frameBuffer->clear(color); depthBuffer->clear(); } void DrawContext::clear_color(const RenderData::Color& color) { frameBuffer->clear(color); } void DrawContext::clear_depth() { depthBuffer->clear(); } void DrawContext::draw_line(const Math::Vector2Int& from, const Math::Vector2Int& to, const RenderData::Color& color) { rasterizer->DrawLine(from, to, color); } void DrawContext::draw_triangle(const RenderData::Triangle& triangle, const RenderData::Color& color) { triangleRasterizer->DrawTriangle2D(triangle, color); } void DrawContext::draw_sprite(int32_t dst_x, int32_t dst_y, const RenderData::Image& img) { draw_sprite_region(dst_x, dst_y, img, 0, 0, img.width, img.height); } void DrawContext::draw_sprite(int32_t dst_x, int32_t dst_y, const RenderData::SpriteRegion& region) { draw_sprite_region(dst_x, dst_y, region); } void DrawContext::draw_sprite_region(int32_t dst_x, int32_t dst_y, const RenderData::Image& img, int32_t src_x, int32_t src_y, int32_t src_w, int32_t src_h) { draw_sprite_ex(dst_x, dst_y, img, src_x, src_y, src_w, src_h, 1, false, false); } void DrawContext::draw_sprite_region(int32_t dst_x, int32_t dst_y, const RenderData::SpriteRegion& region) { draw_sprite_region_ex(dst_x, dst_y, region, 1, false, false); } void DrawContext::draw_sprite_region_ex(int32_t dst_x, int32_t dst_y, const RenderData::SpriteRegion& region, int32_t scale, bool flip_h, bool flip_v) { if (!region.atlas) return; draw_sprite_ex( dst_x, dst_y, *region.atlas, region.x, region.y, region.width, region.height, scale, flip_h, flip_v); } void DrawContext::draw_sprite_ex(int32_t dst_x, int32_t dst_y, const RenderData::Image& img, int32_t src_x, int32_t src_y, int32_t src_w, int32_t src_h, int32_t scale, bool flip_h, bool flip_v) { if (scale < 1 || !img.pixels || src_w <= 0 || src_h <= 0) return; if (src_x < 0 || src_y < 0 || src_x + src_w > img.width || src_y + src_h > img.height) return; const int32_t img_w = img.width; const int32_t screen_w = frameBuffer->get_width(); const int32_t screen_h = frameBuffer->get_height(); const int32_t draw_w = src_w * scale; const int32_t draw_h = src_h * scale; if (dst_x >= screen_w || dst_y >= screen_h || dst_x + draw_w <= 0 || dst_y + draw_h <= 0) return; int32_t start_dx = 0; int32_t start_dy = 0; int32_t end_dx = draw_w; int32_t end_dy = draw_h; if (dst_x < 0) start_dx = -dst_x; if (dst_y < 0) start_dy = -dst_y; if (dst_x + end_dx > screen_w) end_dx = screen_w - dst_x; if (dst_y + end_dy > screen_h) end_dy = screen_h - dst_y; Core::FramePixel* dst = static_cast(frameBuffer->get_buffer()); const int32_t fb_w = frameBuffer->get_width(); const uint16_t* src = static_cast(img.pixels); if (scale == 1) { for (int32_t sy = start_dy; sy < end_dy; ++sy) { const int32_t read_y = flip_v ? (src_y + src_h - 1 - sy) : (src_y + sy); const int32_t dst_y_abs = dst_y + sy; Core::FramePixel* dst_row = dst + dst_y_abs * fb_w; for (int32_t sx = start_dx; sx < end_dx; ++sx) { const int32_t read_x = flip_h ? (src_x + src_w - 1 - sx) : (src_x + sx); const uint16_t pixel = src[read_y * img_w + read_x]; if (!RenderData::rgba5551_is_opaque(pixel)) continue; dst_row[dst_x + sx] = RenderData::rgba5551_to_rgb565(pixel); } } return; } for (int32_t dy_abs = start_dy; dy_abs < end_dy; ++dy_abs) { const int32_t sy = dy_abs / scale; const int32_t read_y = flip_v ? (src_y + src_h - 1 - sy) : (src_y + sy); const int32_t dst_y_abs = dst_y + dy_abs; Core::FramePixel* dst_row = dst + dst_y_abs * fb_w; for (int32_t dx_abs = start_dx; dx_abs < end_dx; ++dx_abs) { const int32_t sx = dx_abs / scale; const int32_t read_x = flip_h ? (src_x + src_w - 1 - sx) : (src_x + sx); const uint16_t pixel = src[read_y * img_w + read_x]; if (!RenderData::rgba5551_is_opaque(pixel)) continue; dst_row[dst_x + dx_abs] = RenderData::rgba5551_to_rgb565(pixel); } } } void DrawContext::draw_tilemap(const RenderData::Tilemap& tilemap, int32_t screen_x, int32_t screen_y, int32_t camera_x, int32_t camera_y) { draw_tilemap(tilemap, screen_x, screen_y, frameBuffer->get_width() - screen_x, frameBuffer->get_height() - screen_y, camera_x, camera_y); } void DrawContext::draw_tilemap(const RenderData::Tilemap& tilemap, int32_t screen_x, int32_t screen_y, int32_t viewport_w, int32_t viewport_h, int32_t camera_x, int32_t camera_y) { if (!tilemap.tiles || !tilemap.atlas || !tilemap.atlas->pixels) return; if (tilemap.width <= 0 || tilemap.height <= 0) return; if (tilemap.tile_w <= 0 || tilemap.tile_h <= 0 || tilemap.atlas_columns <= 0) return; if (viewport_w <= 0 || viewport_h <= 0) return; const int32_t viewport_left = screen_x; const int32_t viewport_top = screen_y; const int32_t viewport_right = screen_x + viewport_w; const int32_t viewport_bottom = screen_y + viewport_h; int32_t start_tile_x = camera_x / tilemap.tile_w; int32_t start_tile_y = camera_y / tilemap.tile_h; int32_t offset_x = -(camera_x % tilemap.tile_w); int32_t offset_y = -(camera_y % tilemap.tile_h); if (camera_x < 0 && camera_x % tilemap.tile_w != 0) { --start_tile_x; offset_x = -camera_x - (-start_tile_x * tilemap.tile_w); } if (camera_y < 0 && camera_y % tilemap.tile_h != 0) { --start_tile_y; offset_y = -camera_y - (-start_tile_y * tilemap.tile_h); } const int32_t visible_cols = viewport_w / tilemap.tile_w + 2; const int32_t visible_rows = viewport_h / tilemap.tile_h + 2; for (int32_t row = 0; row < visible_rows; ++row) { const int32_t map_y = start_tile_y + row; if (map_y < 0 || map_y >= tilemap.height) continue; const int32_t dst_y = screen_y + offset_y + row * tilemap.tile_h; for (int32_t col = 0; col < visible_cols; ++col) { const int32_t map_x = start_tile_x + col; if (map_x < 0 || map_x >= tilemap.width) continue; const uint16_t tile_id = tilemap.get_tile(map_x, map_y); if (tile_id == RenderData::Tilemap::EmptyTile) continue; const int32_t src_x = (tile_id % tilemap.atlas_columns) * tilemap.tile_w; const int32_t src_y = (tile_id / tilemap.atlas_columns) * tilemap.tile_h; const int32_t dst_x = screen_x + offset_x + col * tilemap.tile_w; const int32_t tile_right = dst_x + tilemap.tile_w; const int32_t tile_bottom = dst_y + tilemap.tile_h; int32_t clipped_left = dst_x; int32_t clipped_top = dst_y; int32_t clipped_right = tile_right; int32_t clipped_bottom = tile_bottom; if (clipped_left < viewport_left) clipped_left = viewport_left; if (clipped_top < viewport_top) clipped_top = viewport_top; if (clipped_right > viewport_right) clipped_right = viewport_right; if (clipped_bottom > viewport_bottom) clipped_bottom = viewport_bottom; if (clipped_left >= clipped_right || clipped_top >= clipped_bottom) continue; const int32_t clipped_src_x = src_x + (clipped_left - dst_x); const int32_t clipped_src_y = src_y + (clipped_top - dst_y); const int32_t clipped_w = clipped_right - clipped_left; const int32_t clipped_h = clipped_bottom - clipped_top; draw_sprite_ex(clipped_left, clipped_top, *tilemap.atlas, clipped_src_x, clipped_src_y, clipped_w, clipped_h, 1, false, false); } } } void DrawContext::fill_rect(int32_t x, int32_t y, int32_t w, int32_t h, const RenderData::Color& color) { const uint32_t rgba = color.to_rgba(); const int32_t fb_w = frameBuffer->get_width(); const int32_t fb_h = frameBuffer->get_height(); int32_t x0 = std::max(0, x); int32_t y0 = std::max(0, y); int32_t x1 = std::min(fb_w, x + w); int32_t y1 = std::min(fb_h, y + h); if (x0 >= x1 || y0 >= y1) return; const Core::FramePixel pixel = Core::rgba_to_frame_pixel(rgba); Core::FramePixel* buf = static_cast(frameBuffer->get_buffer()); for (int32_t row = y0; row < y1; ++row) { Core::FramePixel* dst = buf + row * fb_w + x0; for (int32_t i = 0; i < x1 - x0; ++i) { dst[i] = pixel; } } } void DrawContext::draw_font_mask_region(const RenderData::BitmapFont& font, int32_t src_x, int32_t src_y, int32_t src_w, int32_t src_h, int32_t dst_x, int32_t dst_y, uint16_t pixel) { Core::FramePixel* dst = static_cast(frameBuffer->get_buffer()); const int32_t fb_w = frameBuffer->get_width(); for (int32_t row = 0; row < src_h; ++row) { const int32_t atlas_row = src_y + row; const int32_t dst_y_abs = dst_y + row; Core::FramePixel* dst_row = dst + dst_y_abs * fb_w + dst_x; for (int32_t col = 0; col < src_w; ++col) { const int32_t atlas_x = src_x + col; const int32_t bit_index = atlas_row * font.atlas_width + atlas_x; const uint8_t packed = font.mask_bits[bit_index >> 3]; const uint8_t bit_mask = static_cast(0x80u >> (bit_index & 7)); if ((packed & bit_mask) == 0u) continue; dst_row[col] = pixel; } } } void DrawContext::draw_text(const RenderData::BitmapFont& font, int32_t x, int32_t y, const RenderData::Color& color, const char* text) { if (!font.mask_bits || !text) return; const int32_t cw = font.char_w; const int32_t ch = font.char_h; const int32_t atlas_w = font.atlas_width; const int32_t atlas_h = font.atlas_height; const int32_t screen_w = frameBuffer->get_width(); const int32_t screen_h = frameBuffer->get_height(); if (cw <= 0 || ch <= 0 || font.columns <= 0 || atlas_w <= 0 || atlas_h <= 0) return; const Core::FramePixel glyph_pixel = Core::rgba_to_frame_pixel(color.to_rgba()); int32_t cursor_x = x; for (const char* p = text; *p; ++p) { const int32_t index = static_cast(*p) - font.first_char; if (index < 0) { cursor_x += cw; continue; } const int32_t col = index % font.columns; const int32_t row = index / font.columns; const int32_t src_x = col * cw; const int32_t src_y = row * ch; if (src_x < 0 || src_y < 0 || src_x + cw > atlas_w || src_y + ch > atlas_h) { cursor_x += cw; continue; } if (cursor_x + cw <= 0 || cursor_x >= screen_w || y + ch <= 0 || y >= screen_h) { cursor_x += cw; continue; } int32_t sy_start = 0, sy_end = ch; int32_t sx_start = 0, sx_end = cw; if (y < 0) sy_start = -y; if (y + ch > screen_h) sy_end = screen_h - y; if (cursor_x < 0) sx_start = -cursor_x; if (cursor_x + cw > screen_w) sx_end = screen_w - cursor_x; draw_font_mask_region(font, src_x + sx_start, src_y + sy_start, sx_end - sx_start, sy_end - sy_start, cursor_x + sx_start, y + sy_start, glyph_pixel); cursor_x += cw; } } void DrawContext::draw_text(const RenderData::BitmapFont& font, int32_t x, int32_t y, const RenderData::Color& color, const RenderData::Color& bg_color, const char* text) { if (!text) return; int32_t len = 0; for (const char* p = text; *p; ++p) ++len; fill_rect(x, y, len * font.char_w, font.char_h, bg_color); draw_text(font, x, y, color, text); } void DrawContext::present(Platform::IDisplay* display) { display->present(frameBuffer); } }