Gfx.lua (12925B)
1 -- Graphics Library for LuajitOS 2 -- Provides high-level drawing functions using VGA Mode 13h (320x200, 256 colors) 3 4 local gfx = {} 5 6 -- Graphics mode enum 7 gfx.MODE_VGA = 0 8 gfx.MODE_VESA = 1 9 10 -- Screen dimensions (default to VGA) 11 gfx.WIDTH = 320 12 gfx.HEIGHT = 200 13 14 -- Graphics mode state 15 gfx.enabled = false 16 gfx.currentMode = gfx.MODE_VGA -- Default to VGA Mode 13h 17 18 -- Draw operation type constants 19 local PIXEL = 0 20 local RECT = 1 21 local RECT_FILL = 2 22 local CIRCLE = 3 23 local CIRCLE_FILL = 4 24 local TRIANGLE = 5 25 local TRIANGLE_FILL = 6 26 local POLYGON = 7 27 local POLYGON_FILL = 8 28 local LINE = 9 29 30 -- Buffer state 31 gfx.buffer = { 32 enable = false, 33 operations = {} 34 } 35 36 -- Helper function to clamp coordinates 37 local function clamp(val, min, max) 38 if val < min then return min end 39 if val > max then return max end 40 return val 41 end 42 43 -- Draw a single pixel 44 function gfx.pixel(x, y, color) 45 if gfx.buffer.enable then 46 table.insert(gfx.buffer.operations, {PIXEL, x, y, color}) 47 else 48 SetPixel(x, y, color) 49 end 50 end 51 52 -- Draw a filled rectangle 53 function gfx.fillRect(x, y, width, height, color) 54 if gfx.buffer.enable then 55 table.insert(gfx.buffer.operations, {RECT_FILL, x, y, width, height, color}) 56 else 57 DrawRect(x, y, width, height, color) 58 end 59 end 60 61 -- Draw a line using Bresenham's algorithm 62 function gfx.line(x0, y0, x1, y1, color, thickness) 63 thickness = thickness or 1 64 65 if gfx.buffer.enable then 66 -- Buffer as a single LINE operation 67 table.insert(gfx.buffer.operations, {LINE, x0, y0, x1, y1, color, thickness}) 68 else 69 local dx = x1 - x0 70 local dy = y1 - y0 71 72 -- Handle vertical and horizontal lines specially for thickness 73 if dx == 0 then 74 -- Vertical line 75 local startY = y0 < y1 and y0 or y1 76 local endY = y0 < y1 and y1 or y0 77 for ty = startY, endY do 78 for tx = 0, thickness - 1 do 79 SetPixel(x0 + tx, ty, color) 80 end 81 end 82 return 83 end 84 85 if dy == 0 then 86 -- Horizontal line 87 local startX = x0 < x1 and x0 or x1 88 local endX = x0 < x1 and x1 or x0 89 for tx = startX, endX do 90 for ty = 0, thickness - 1 do 91 SetPixel(tx, y0 + ty, color) 92 end 93 end 94 return 95 end 96 97 -- Bresenham's line algorithm 98 local dx_abs = dx < 0 and -dx or dx 99 local dy_abs = dy < 0 and -dy or dy 100 local sx = dx > 0 and 1 or -1 101 local sy = dy > 0 and 1 or -1 102 local err = dx_abs - dy_abs 103 104 local x = x0 105 local y = y0 106 107 while true do 108 -- Draw with thickness 109 for ty = 0, thickness - 1 do 110 for tx = 0, thickness - 1 do 111 SetPixel(x + tx, y + ty, color) 112 end 113 end 114 115 if x == x1 and y == y1 then break end 116 117 local e2 = 2 * err 118 if e2 > -dy_abs then 119 err = err - dy_abs 120 x = x + sx 121 end 122 if e2 < dx_abs then 123 err = err + dx_abs 124 y = y + sy 125 end 126 end 127 end 128 end 129 130 -- Draw an outlined rectangle 131 function gfx.rect(x, y, width, height, color, thickness) 132 thickness = thickness or 1 133 134 if gfx.buffer.enable then 135 table.insert(gfx.buffer.operations, {RECT, x, y, width, height, color, thickness}) 136 else 137 -- Top and bottom edges 138 for i = 0, thickness - 1 do 139 DrawRect(x, y + i, width, 1, color) 140 DrawRect(x, y + height - 1 - i, width, 1, color) 141 end 142 143 -- Left and right edges 144 for i = 0, thickness - 1 do 145 DrawRect(x + i, y, 1, height, color) 146 DrawRect(x + width - 1 - i, y, 1, height, color) 147 end 148 end 149 end 150 151 -- Draw a filled circle using midpoint algorithm 152 function gfx.fillCircle(cx, cy, radius, color) 153 if gfx.buffer.enable then 154 table.insert(gfx.buffer.operations, {CIRCLE_FILL, cx, cy, radius, color}) 155 else 156 local x = radius 157 local y = 0 158 local err = 0 159 160 while x >= y do 161 -- Draw horizontal lines for each octant 162 DrawRect(cx - x, cy + y, 2 * x + 1, 1, color) 163 DrawRect(cx - x, cy - y, 2 * x + 1, 1, color) 164 DrawRect(cx - y, cy + x, 2 * y + 1, 1, color) 165 DrawRect(cx - y, cy - x, 2 * y + 1, 1, color) 166 167 y = y + 1 168 err = err + 1 + 2 * y 169 if 2 * (err - x) + 1 > 0 then 170 x = x - 1 171 err = err + 1 - 2 * x 172 end 173 end 174 end 175 end 176 177 -- Draw an outlined circle using midpoint algorithm 178 function gfx.circle(cx, cy, radius, color, thickness) 179 thickness = thickness or 1 180 181 if gfx.buffer.enable then 182 table.insert(gfx.buffer.operations, {CIRCLE, cx, cy, radius, color, thickness}) 183 else 184 for t = 0, thickness - 1 do 185 local r = radius - t 186 if r < 0 then break end 187 188 local x = r 189 local y = 0 190 local err = 0 191 192 while x >= y do 193 -- Draw 8 symmetric points 194 SetPixel(cx + x, cy + y, color) 195 SetPixel(cx + y, cy + x, color) 196 SetPixel(cx - y, cy + x, color) 197 SetPixel(cx - x, cy + y, color) 198 SetPixel(cx - x, cy - y, color) 199 SetPixel(cx - y, cy - x, color) 200 SetPixel(cx + y, cy - x, color) 201 SetPixel(cx + x, cy - y, color) 202 203 y = y + 1 204 err = err + 1 + 2 * y 205 if 2 * (err - x) + 1 > 0 then 206 x = x - 1 207 err = err + 1 - 2 * x 208 end 209 end 210 end 211 end 212 end 213 214 -- Draw a filled triangle 215 function gfx.fillTriangle(x0, y0, x1, y1, x2, y2, color) 216 if gfx.buffer.enable then 217 table.insert(gfx.buffer.operations, {TRIANGLE_FILL, x0, y0, x1, y1, x2, y2, color}) 218 else 219 -- Sort vertices by y-coordinate (y0 <= y1 <= y2) 220 if y0 > y1 then 221 x0, y0, x1, y1 = x1, y1, x0, y0 222 end 223 if y0 > y2 then 224 x0, y0, x2, y2 = x2, y2, x0, y0 225 end 226 if y1 > y2 then 227 x1, y1, x2, y2 = x2, y2, x1, y1 228 end 229 230 -- Draw flat-bottom triangle (y0 to y1) 231 if y1 - y0 > 0 then 232 local invslope1 = (x1 - x0) / (y1 - y0) 233 local invslope2 = (x2 - x0) / (y2 - y0) 234 local curx1 = x0 235 local curx2 = x0 236 237 for scanlineY = y0, y1 do 238 local startX = curx1 < curx2 and curx1 or curx2 239 local endX = curx1 < curx2 and curx2 or curx1 240 DrawRect(startX, scanlineY, endX - startX + 1, 1, color) 241 curx1 = curx1 + invslope1 242 curx2 = curx2 + invslope2 243 end 244 end 245 246 -- Draw flat-top triangle (y1 to y2) 247 if y2 - y1 > 0 then 248 local invslope1 = (x2 - x1) / (y2 - y1) 249 local invslope2 = (x2 - x0) / (y2 - y0) 250 local curx1 = x1 251 local curx2 = x0 + (y1 - y0) * ((x2 - x0) / (y2 - y0)) 252 253 for scanlineY = y1, y2 do 254 local startX = curx1 < curx2 and curx1 or curx2 255 local endX = curx1 < curx2 and curx2 or curx1 256 DrawRect(startX, scanlineY, endX - startX + 1, 1, color) 257 curx1 = curx1 + invslope1 258 curx2 = curx2 + invslope2 259 end 260 end 261 end 262 end 263 264 -- Draw an outlined triangle 265 function gfx.triangle(x0, y0, x1, y1, x2, y2, color, thickness) 266 thickness = thickness or 1 267 if gfx.buffer.enable then 268 table.insert(gfx.buffer.operations, {TRIANGLE, x0, y0, x1, y1, x2, y2, color, thickness}) 269 else 270 gfx.line(x0, y0, x1, y1, color, thickness) 271 gfx.line(x1, y1, x2, y2, color, thickness) 272 gfx.line(x2, y2, x0, y0, color, thickness) 273 end 274 end 275 276 -- Draw a filled polygon (arbitrary number of vertices) 277 function gfx.fillPolygon(vertices, color) 278 if #vertices < 3 then return end 279 280 if gfx.buffer.enable then 281 -- Store polygon vertices as flat array: {POLYGON_FILL, numVertices, x1, y1, x2, y2, ..., color} 282 local operation = {POLYGON_FILL, #vertices} 283 for i = 1, #vertices do 284 table.insert(operation, vertices[i].x) 285 table.insert(operation, vertices[i].y) 286 end 287 table.insert(operation, color) 288 table.insert(gfx.buffer.operations, operation) 289 else 290 -- Find min and max Y 291 local minY = vertices[1].y 292 local maxY = vertices[1].y 293 for i = 1, #vertices do 294 if vertices[i].y < minY then minY = vertices[i].y end 295 if vertices[i].y > maxY then maxY = vertices[i].y end 296 end 297 298 -- Scanline fill 299 for y = minY, maxY do 300 local intersections = {} 301 302 -- Find intersections with all edges 303 for i = 1, #vertices do 304 local j = (i % #vertices) + 1 305 local x1, y1 = vertices[i].x, vertices[i].y 306 local x2, y2 = vertices[j].x, vertices[j].y 307 308 if (y1 <= y and y < y2) or (y2 <= y and y < y1) then 309 -- Calculate x intersection 310 local x = x1 + (y - y1) * (x2 - x1) / (y2 - y1) 311 table.insert(intersections, x) 312 end 313 end 314 315 -- Sort intersections 316 table.sort(intersections) 317 318 -- Fill between pairs 319 for i = 1, #intersections - 1, 2 do 320 local startX = intersections[i] 321 local endX = intersections[i + 1] 322 if endX >= startX then 323 DrawRect(startX, y, endX - startX + 1, 1, color) 324 end 325 end 326 end 327 end 328 end 329 330 -- Draw an outlined polygon 331 function gfx.polygon(vertices, color, thickness) 332 if #vertices < 2 then return end 333 thickness = thickness or 1 334 335 if gfx.buffer.enable then 336 -- Store polygon vertices as flat array: {POLYGON, numVertices, x1, y1, x2, y2, ..., color, thickness} 337 local operation = {POLYGON, #vertices} 338 for i = 1, #vertices do 339 table.insert(operation, vertices[i].x) 340 table.insert(operation, vertices[i].y) 341 end 342 table.insert(operation, color) 343 table.insert(operation, thickness) 344 table.insert(gfx.buffer.operations, operation) 345 else 346 for i = 1, #vertices do 347 local j = (i % #vertices) + 1 348 gfx.line(vertices[i].x, vertices[i].y, vertices[j].x, vertices[j].y, color, thickness) 349 end 350 end 351 end 352 353 -- Clear screen 354 function gfx.clear(color) 355 ClearScreen(color or 0) 356 end 357 358 -- Enter graphics mode (VGA Mode 13h - 320x200) 359 function gfx.init() 360 EnterGraphicsMode() 361 gfx.enabled = true 362 gfx.currentMode = gfx.MODE_VGA 363 gfx.WIDTH = 320 364 gfx.HEIGHT = 200 365 end 366 367 -- Enter VESA mode with specified resolution 368 -- Falls back to VGA if VESA initialization fails 369 function gfx.initVESA(width, height, bpp) 370 width = width or 1024 371 height = height or 768 372 bpp = bpp or 32 373 374 -- Try to initialize VESA mode 375 local success = VESASetMode(width, height, bpp) 376 377 if success then 378 gfx.enabled = true 379 gfx.currentMode = gfx.MODE_VESA 380 gfx.WIDTH = width 381 gfx.HEIGHT = height 382 gfx.BPP = bpp 383 return true 384 else 385 -- Fallback to VGA Mode 13h 386 osprint("VESA initialization failed, falling back to VGA Mode 13h\n") 387 gfx.init() 388 return false 389 end 390 end 391 392 -- Exit graphics mode 393 function gfx.close() 394 if gfx.currentMode == gfx.MODE_VESA then 395 -- VESA mode - no specific exit needed, just clear state 396 gfx.enabled = false 397 gfx.currentMode = gfx.MODE_VGA 398 else 399 -- VGA Mode 13h 400 ExitGraphicsMode() 401 gfx.enabled = false 402 end 403 end 404 405 -- Check if currently using VESA mode 406 function gfx.isVESA() 407 return gfx.currentMode == gfx.MODE_VESA 408 end 409 410 -- Get current mode info 411 function gfx.getModeInfo() 412 if gfx.currentMode == gfx.MODE_VESA then 413 return VESAGetModeInfo() 414 else 415 return { 416 width = 320, 417 height = 200, 418 bpp = 8, 419 mode = "VGA Mode 13h" 420 } 421 end 422 end 423 424 -- Draw all buffered operations and clear the buffer 425 function gfx.buffer.drawAll() 426 if #gfx.buffer.operations > 0 then 427 -- Call appropriate C function based on current mode 428 if gfx.currentMode == gfx.MODE_VESA then 429 VESAProcessBufferedDrawOps(gfx.buffer.operations) 430 else 431 ProcessBufferedDrawOps(gfx.buffer.operations) 432 end 433 -- Clear the buffer 434 gfx.buffer.operations = {} 435 end 436 end 437 438 -- Clear the buffer without drawing 439 function gfx.buffer.clear() 440 gfx.buffer.operations = {} 441 end 442 443 return gfx