luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

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