Advertisement
Omsigames

grapes/Screen.lua

Dec 25th, 2024
38
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 27.60 KB | None | 0 0
  1.  
  2. local color = require("grapes.Color")
  3. local image = require("grapes.Image")
  4. local component = require("component")
  5. local unicode = require("unicode")
  6.  
  7. --------------------------------------------------------------------------------
  8.  
  9. local
  10.     componentInvoke
  11.  
  12.     mathCeil,
  13.     mathFloor,
  14.     mathModf,
  15.     mathAbs,
  16.     mathMin,
  17.     mathMax
  18.     =
  19.     component.invoke,
  20.  
  21.     math.ceil,
  22.     math.floor,
  23.     math.modf,
  24.     math.abs,
  25.     math.min,
  26.     math.max;
  27.  
  28. local
  29.     bufferWidth,
  30.     bufferHeight,
  31.  
  32.     currentFrameBackgrounds,
  33.     currentFrameForegrounds,
  34.     currentFrameSymbols,
  35.     newFrameBackgrounds,
  36.     newFrameForegrounds,
  37.     newFrameSymbols,
  38.  
  39.     drawLimitX1,
  40.     drawLimitX2,
  41.     drawLimitY1,
  42.     drawLimitY2,
  43.  
  44.     GPUAddress;
  45.  
  46. componentInvoke = component.invoke
  47. --------------------------------------------------------------------------------
  48.  
  49. local function getIndex(x, y)
  50.     return bufferWidth * (y - 1) + x
  51. end
  52.  
  53. local function getCurrentFrameTables()
  54.     return currentFrameBackgrounds, currentFrameForegrounds, currentFrameSymbols
  55. end
  56.  
  57. local function getNewFrameTables()
  58.     return newFrameBackgrounds, newFrameForegrounds, newFrameSymbols
  59. end
  60.  
  61. --------------------------------------------------------------------------------
  62.  
  63. local function setDrawLimit(x1, y1, x2, y2)
  64.     drawLimitX1, drawLimitY1, drawLimitX2, drawLimitY2 = x1, y1, x2, y2
  65. end
  66.  
  67. local function resetDrawLimit()
  68.     drawLimitX1, drawLimitY1, drawLimitX2, drawLimitY2 = 1, 1, bufferWidth, bufferHeight
  69. end
  70.  
  71. local function getDrawLimit()
  72.     return drawLimitX1, drawLimitY1, drawLimitX2, drawLimitY2
  73. end
  74.  
  75. --------------------------------------------------------------------------------
  76.  
  77. local function flush(width, height)
  78.     if not width or not height then
  79.         width, height = componentInvoke(GPUAddress, "getResolution")
  80.     end
  81.  
  82.     currentFrameBackgrounds, currentFrameForegrounds, currentFrameSymbols, newFrameBackgrounds, newFrameForegrounds, newFrameSymbols = {}, {}, {}, {}, {}, {}
  83.     bufferWidth = width
  84.     bufferHeight = height
  85.  
  86.     resetDrawLimit()
  87.  
  88.     for i = 1, bufferWidth * bufferHeight do
  89.         currentFrameBackgrounds[i] = 0x010101
  90.         newFrameBackgrounds[i] = 0x010101
  91.  
  92.         currentFrameForegrounds[i] = 0xFEFEFE
  93.         newFrameForegrounds[i] = 0xFEFEFE
  94.  
  95.         currentFrameSymbols[i] = " "
  96.         newFrameSymbols[i] = " "
  97.     end
  98. end
  99.  
  100. local function getGPUAddress()
  101.     return GPUAddress
  102. end
  103.  
  104. local function setGPUAddress(address)
  105.     GPUAddress = address
  106.  
  107.     flush()
  108. end
  109.  
  110. local function getScreenAddress()
  111.     return componentInvoke(GPUAddress, "getScreen")
  112. end
  113.  
  114. local function getMaxResolution()
  115.     return componentInvoke(GPUAddress, "maxResolution")
  116. end
  117.  
  118. local function setResolution(width, height)
  119.     componentInvoke(GPUAddress, "setResolution", width, height)
  120.    
  121.     flush(width, height)
  122. end
  123.  
  124. local function getColorDepth()
  125.     return componentInvoke(GPUAddress, "getDepth")
  126. end
  127.  
  128. local function setColorDepth(...)
  129.     return componentInvoke(GPUAddress, "setDepth", ...)
  130. end
  131.  
  132. local function getMaxColorDepth(...)
  133.     return componentInvoke(GPUAddress, "maxDepth")
  134. end
  135.  
  136. local function getScreenAspectRatio()
  137.     return componentInvoke(getScreenAddress(), "getAspectRatio")
  138. end
  139.  
  140. local function getResolution()
  141.     return bufferWidth, bufferHeight
  142. end
  143.  
  144. local function getWidth()
  145.     return bufferWidth
  146. end
  147.  
  148. local function getHeight()
  149.     return bufferHeight
  150. end
  151.  
  152. local function setScreenAddress(address, reset)
  153.     local success, reason = componentInvoke(GPUAddress, "bind", address, reset)
  154.  
  155.     if success then
  156.         if reset then
  157.             setResolution(getMaxResolution())
  158.         else
  159.             setResolution(bufferWidth, bufferHeight)
  160.         end
  161.     else
  162.         return success, reason
  163.     end
  164. end
  165.  
  166. local function getScaledResolution(scale)
  167.     if not scale or scale > 1 then
  168.         scale = 1
  169.     elseif scale < 0.1 then
  170.         scale = 0.1
  171.     end
  172.    
  173.     local aspectWidth, aspectHeight = getScreenAspectRatio()
  174.     local maxWidth, maxHeight = getMaxResolution()
  175.     local proportion = 2 * (16 * aspectWidth - 4.5) / (16 * aspectHeight - 4.5)
  176.      
  177.     local height = scale * mathMin(
  178.         maxWidth / proportion,
  179.         maxWidth,
  180.         math.sqrt(maxWidth * maxHeight / proportion)
  181.     )
  182.  
  183.     return math.floor(height * proportion), math.floor(height)
  184. end
  185.  
  186. --------------------------------------------------------------------------------
  187.  
  188. local function rawSet(index, background, foreground, symbol)
  189.     newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, foreground, symbol
  190. end
  191.  
  192. local function rawGet(index)
  193.     return newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index]
  194. end
  195.  
  196. local function get(x, y)
  197.     if x >= 1 and y >= 1 and x <= bufferWidth and y <= bufferHeight then
  198.         local index = bufferWidth * (y - 1) + x
  199.         return newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index]
  200.     else
  201.         return 0x000000, 0x000000, " "
  202.     end
  203. end
  204.  
  205. local function set(x, y, background, foreground, symbol)
  206.     if x >= drawLimitX1 and y >= drawLimitY1 and x <= drawLimitX2 and y <= drawLimitY2 then
  207.         local index = bufferWidth * (y - 1) + x
  208.         newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, foreground, symbol
  209.     end
  210. end
  211.  
  212. local function drawRectangle(x, y, width, height, background, foreground, symbol, transparency)
  213.     local temp
  214.  
  215.     -- Clipping left
  216.     if x < drawLimitX1 then
  217.         width = width - drawLimitX1 + x
  218.         x = drawLimitX1
  219.     end
  220.  
  221.     -- Right
  222.     temp = x + width - 1
  223.     if temp > drawLimitX2 then
  224.         width = width - temp + drawLimitX2
  225.     end
  226.  
  227.     -- Top
  228.     if y < drawLimitY1 then
  229.         height = height - drawLimitY1 + y
  230.         y = drawLimitY1
  231.     end
  232.  
  233.     -- Bottom
  234.     temp = y + height - 1
  235.     if temp > drawLimitY2 then
  236.         height = height - temp + drawLimitY2
  237.     end
  238.  
  239.     temp = bufferWidth * (y - 1) + x
  240.  
  241.     local indexStepOnEveryLine = bufferWidth - width
  242.  
  243.     if transparency then
  244.         for j = 1, height do
  245.             for i = 1, width do
  246.                 newFrameBackgrounds[temp],
  247.                 newFrameForegrounds[temp] =
  248.                     color.blend(newFrameBackgrounds[temp], background, transparency),
  249.                     color.blend(newFrameForegrounds[temp], background, transparency)
  250.  
  251.                 temp = temp + 1
  252.             end
  253.  
  254.             temp = temp + indexStepOnEveryLine
  255.         end
  256.     else
  257.         for j = 1, height do
  258.             for i = 1, width do
  259.                 newFrameBackgrounds[temp],
  260.                 newFrameForegrounds[temp],
  261.                 newFrameSymbols[temp] = background, foreground, symbol
  262.  
  263.                 temp = temp + 1
  264.             end
  265.  
  266.             temp = temp + indexStepOnEveryLine
  267.         end
  268.     end
  269. end
  270.  
  271. local function blur(x, y, width, height, radius, color, transparency)
  272.     local temp
  273.  
  274.     -- Clipping left
  275.     if x < drawLimitX1 then
  276.         width = width - drawLimitX1 + x
  277.         x = drawLimitX1
  278.     end
  279.  
  280.     -- Right
  281.     temp = x + width - 1
  282.     if temp > drawLimitX2 then
  283.         width = width - temp + drawLimitX2
  284.     end
  285.  
  286.     -- Top
  287.     if y < drawLimitY1 then
  288.         height = height - drawLimitY1 + y
  289.         y = drawLimitY1
  290.     end
  291.  
  292.     -- Bottom
  293.     temp = y + height - 1
  294.     if temp > drawLimitY2 then
  295.         height = height - temp + drawLimitY2
  296.     end
  297.  
  298.     local screenIndex, indexStepOnEveryLine, buffer, bufferIndex, rSum, gSum, bSum, rSumFg, gSumFg, bSumFg, r, g, b =
  299.         bufferWidth * (y - 1) + x,
  300.         bufferWidth - width,
  301.         {},
  302.         1
  303.  
  304.     -- Copying
  305.     temp = screenIndex
  306.  
  307.     if color then
  308.         for j = 1, height do
  309.             for i = 1, width do
  310.                 buffer[bufferIndex] = color.blend(newFrameBackgrounds[temp], color, transparency)
  311.  
  312.                 temp, bufferIndex = temp + 1, bufferIndex + 1
  313.             end
  314.  
  315.             temp = temp + indexStepOnEveryLine
  316.         end
  317.     else
  318.         for j = 1, height do
  319.             for i = 1, width do
  320.                 buffer[bufferIndex] = newFrameBackgrounds[temp]
  321.  
  322.                 temp, bufferIndex = temp + 1, bufferIndex + 1
  323.             end
  324.  
  325.             temp = temp + indexStepOnEveryLine
  326.         end
  327.     end
  328.  
  329.     -- Blurring
  330.     local rSum, gSum, bSum, count, r, g, b
  331.  
  332.     for j = 1, height do
  333.         for i = 1, width do
  334.             rSum, gSum, bSum, count = 0, 0, 0, 0
  335.  
  336.             for jr = mathMax(1, j - radius), mathMin(j + radius, height) do
  337.                 for ir = mathMax(1, i - radius), mathMin(i + radius, width) do
  338.                     r, g, b = color.IntegerToRGB(buffer[width * (jr - 1) + ir])
  339.                     rSum, gSum, bSum, count = rSum + r, gSum + g, bSum + b, count + 1
  340.                 end
  341.             end
  342.  
  343.             -- Calculatin average channels value
  344.             r, g, b = rSum / count, gSum / count, bSum / count
  345.             -- Faster than math.floor
  346.             r, g, b = r - r % 1, g - g % 1, b - b % 1
  347.  
  348.             newFrameBackgrounds[screenIndex] = color.RGBToInteger(r, g, b)
  349.             newFrameForegrounds[screenIndex] = 0x0
  350.             newFrameSymbols[screenIndex] = " "
  351.  
  352.             screenIndex = screenIndex + 1
  353.         end
  354.  
  355.         screenIndex = screenIndex + indexStepOnEveryLine
  356.     end
  357. end
  358.  
  359. local function clear(color, transparency)
  360.     drawRectangle(1, 1, bufferWidth, bufferHeight, color or 0x0, 0x000000, " ", transparency)
  361. end
  362.  
  363. local function copy(x, y, width, height)
  364.     local copyArray, index = { width, height }
  365.  
  366.     for j = y, y + height - 1 do
  367.         for i = x, x + width - 1 do
  368.             if i >= 1 and j >= 1 and i <= bufferWidth and j <= bufferHeight then
  369.                 index = bufferWidth * (j - 1) + i
  370.                 table.insert(copyArray, newFrameBackgrounds[index])
  371.                 table.insert(copyArray, newFrameForegrounds[index])
  372.                 table.insert(copyArray, newFrameSymbols[index])
  373.             else
  374.                 table.insert(copyArray, 0x0)
  375.                 table.insert(copyArray, 0x0)
  376.                 table.insert(copyArray, " ")
  377.             end
  378.         end
  379.     end
  380.  
  381.     return copyArray
  382. end
  383.  
  384. local function paste(startX, startY, picture)
  385.     local imageWidth = picture[1]
  386.     local screenIndex, pictureIndex, screenIndexStepOnReachOfImageWidth = bufferWidth * (startY - 1) + startX, 3, bufferWidth - imageWidth
  387.  
  388.     for y = startY, startY + picture[2] - 1 do
  389.         if y >= drawLimitY1 and y <= drawLimitY2 then
  390.             for x = startX, startX + imageWidth - 1 do
  391.                 if x >= drawLimitX1 and x <= drawLimitX2 then
  392.                     newFrameBackgrounds[screenIndex] = picture[pictureIndex]
  393.                     newFrameForegrounds[screenIndex] = picture[pictureIndex + 1]
  394.                     newFrameSymbols[screenIndex] = picture[pictureIndex + 2]
  395.                 end
  396.  
  397.                 screenIndex, pictureIndex = screenIndex + 1, pictureIndex + 3
  398.             end
  399.  
  400.             screenIndex = screenIndex + screenIndexStepOnReachOfImageWidth
  401.         else
  402.             screenIndex, pictureIndex = screenIndex + bufferWidth, pictureIndex + imageWidth * 3
  403.         end
  404.     end
  405. end
  406.  
  407. local function rasterizeLine(x1, y1, x2, y2, method)
  408.     local inLoopValueFrom, inLoopValueTo, outLoopValueFrom, outLoopValueTo, isReversed, inLoopValueDelta, outLoopValueDelta = x1, x2, y1, y2, false, mathAbs(x2 - x1), mathAbs(y2 - y1)
  409.     if inLoopValueDelta < outLoopValueDelta then
  410.         inLoopValueFrom, inLoopValueTo, outLoopValueFrom, outLoopValueTo, isReversed, inLoopValueDelta, outLoopValueDelta = y1, y2, x1, x2, true, outLoopValueDelta, inLoopValueDelta
  411.     end
  412.  
  413.     if outLoopValueFrom > outLoopValueTo then
  414.         outLoopValueFrom, outLoopValueTo = outLoopValueTo, outLoopValueFrom
  415.         inLoopValueFrom, inLoopValueTo = inLoopValueTo, inLoopValueFrom
  416.     end
  417.  
  418.     local outLoopValue, outLoopValueCounter, outLoopValueTriggerIncrement = outLoopValueFrom, 1, inLoopValueDelta / outLoopValueDelta
  419.     local outLoopValueTrigger = outLoopValueTriggerIncrement
  420.    
  421.     for inLoopValue = inLoopValueFrom, inLoopValueTo, inLoopValueFrom < inLoopValueTo and 1 or -1 do
  422.         if isReversed then
  423.             method(outLoopValue, inLoopValue)
  424.         else
  425.             method(inLoopValue, outLoopValue)
  426.         end
  427.  
  428.         outLoopValueCounter = outLoopValueCounter + 1
  429.         if outLoopValueCounter > outLoopValueTrigger then
  430.             outLoopValue, outLoopValueTrigger = outLoopValue + 1, outLoopValueTrigger + outLoopValueTriggerIncrement
  431.         end
  432.     end
  433. end
  434.  
  435. local function rasterizeEllipse(centerX, centerY, radiusX, radiusY, method)
  436.     local function rasterizeEllipsePoints(XP, YP)
  437.         method(centerX + XP, centerY + YP)
  438.         method(centerX - XP, centerY + YP)
  439.         method(centerX - XP, centerY - YP)
  440.         method(centerX + XP, centerY - YP)
  441.     end
  442.  
  443.     local x, y, changeX, changeY, ellipseError, twoASquare, twoBSquare = radiusX, 0, radiusY * radiusY * (1 - 2 * radiusX), radiusX * radiusX, 0, 2 * radiusX * radiusX, 2 * radiusY * radiusY
  444.     local stoppingX, stoppingY = twoBSquare * radiusX, 0
  445.  
  446.     while stoppingX >= stoppingY do
  447.         rasterizeEllipsePoints(x, y)
  448.        
  449.         y, stoppingY, ellipseError = y + 1, stoppingY + twoASquare, ellipseError + changeY
  450.         changeY = changeY + twoASquare
  451.  
  452.         if (2 * ellipseError + changeX) > 0 then
  453.             x, stoppingX, ellipseError = x - 1, stoppingX - twoBSquare, ellipseError + changeX
  454.             changeX = changeX + twoBSquare
  455.         end
  456.     end
  457.  
  458.     x, y, changeX, changeY, ellipseError, stoppingX, stoppingY = 0, radiusY, radiusY * radiusY, radiusX * radiusX * (1 - 2 * radiusY), 0, 0, twoASquare * radiusY
  459.  
  460.     while stoppingX <= stoppingY do
  461.         rasterizeEllipsePoints(x, y)
  462.        
  463.         x, stoppingX, ellipseError = x + 1, stoppingX + twoBSquare, ellipseError + changeX
  464.         changeX = changeX + twoBSquare
  465.        
  466.         if (2 * ellipseError + changeY) > 0 then
  467.             y, stoppingY, ellipseError = y - 1, stoppingY - twoASquare, ellipseError + changeY
  468.             changeY = changeY + twoASquare
  469.         end
  470.     end
  471. end
  472.  
  473. local function rasterizePolygon(centerX, centerY, startX, startY, countOfEdges, method)
  474.     local degreeStep = 360 / countOfEdges
  475.  
  476.     local deltaX, deltaY = startX - centerX, startY - centerY
  477.     local radius = math.sqrt(deltaX ^ 2 + deltaY ^ 2)
  478.     local halfRadius = radius / 2
  479.     local startDegree = math.deg(math.asin(deltaX / radius))
  480.  
  481.     local function round(num)
  482.         if num >= 0 then
  483.             return math.floor(num + 0.5)
  484.         else
  485.             return math.ceil(num - 0.5)
  486.         end
  487.     end
  488.  
  489.     local function calculatePosition(degree)
  490.         local radDegree = math.rad(degree)
  491.         local deltaX2 = math.sin(radDegree) * radius
  492.         local deltaY2 = math.cos(radDegree) * radius
  493.        
  494.         return round(centerX + deltaX2), round(centerY + (deltaY >= 0 and deltaY2 or -deltaY2))
  495.     end
  496.  
  497.     local xOld, yOld, xNew, yNew = calculatePosition(startDegree)
  498.  
  499.     for degree = (startDegree + degreeStep - 1), (startDegree + 360), degreeStep do
  500.         xNew, yNew = calculatePosition(degree)
  501.         rasterizeLine(xOld, yOld, xNew, yNew, method)
  502.         xOld, yOld = xNew, yNew
  503.     end
  504. end
  505.  
  506. local function drawLine(x1, y1, x2, y2, background, foreground, symbol)
  507.     rasterizeLine(x1, y1, x2, y2, function(x, y)
  508.         set(x, y, background, foreground, symbol)
  509.     end)
  510. end
  511.  
  512. local function drawEllipse(centerX, centerY, radiusX, radiusY, background, foreground, symbol)
  513.     rasterizeEllipse(centerX, centerY, radiusX, radiusY, function(x, y)
  514.         set(x, y, background, foreground, symbol)
  515.     end)
  516. end
  517.  
  518. local function drawPolygon(centerX, centerY, radiusX, radiusY, background, foreground, countOfEdges, symbol)
  519.     rasterizePolygon(centerX, centerY, radiusX, radiusY, countOfEdges, function(x, y)
  520.         set(x, y, background, foreground, symbol)
  521.     end)
  522. end
  523.  
  524. local function drawText(x, y, textColor, data, transparency)
  525.     if y >= drawLimitY1 and y <= drawLimitY2 then
  526.         local charIndex, screenIndex = 1, bufferWidth * (y - 1) + x
  527.        
  528.         for charIndex = 1, unicode.len(data) do
  529.             if x >= drawLimitX1 and x <= drawLimitX2 then
  530.                 if transparency then
  531.                     newFrameForegrounds[screenIndex] = color.blend(newFrameBackgrounds[screenIndex], textColor, transparency)
  532.                 else
  533.                     newFrameForegrounds[screenIndex] = textColor
  534.                 end
  535.  
  536.                 newFrameSymbols[screenIndex] = unicode.sub(data, charIndex, charIndex)
  537.             end
  538.  
  539.             x, screenIndex = x + 1, screenIndex + 1
  540.         end
  541.     end
  542. end
  543.  
  544. local function drawImage(x, y, picture, blendForeground)
  545.     local imageWidth, imageHeight, pictureIndex, temp = picture[1], picture[2], 3
  546.     local clippedImageWidth, clippedImageHeight = imageWidth, imageHeight
  547.  
  548.     -- Clipping left
  549.     if x < drawLimitX1 then
  550.         temp = drawLimitX1 - x
  551.         clippedImageWidth, x, pictureIndex = clippedImageWidth - temp, drawLimitX1, pictureIndex + temp * 4
  552.     end
  553.  
  554.     -- Right
  555.     temp = x + clippedImageWidth - 1
  556.    
  557.     if temp > drawLimitX2 then
  558.         clippedImageWidth = clippedImageWidth - temp + drawLimitX2
  559.     end
  560.  
  561.     -- Top
  562.     if y < drawLimitY1 then
  563.         temp = drawLimitY1 - y
  564.         clippedImageHeight, y, pictureIndex = clippedImageHeight - temp, drawLimitY1, pictureIndex + temp * imageWidth * 4
  565.     end
  566.  
  567.     -- Bottom
  568.     temp = y + clippedImageHeight - 1
  569.    
  570.     if temp > drawLimitY2 then
  571.         clippedImageHeight = clippedImageHeight - temp + drawLimitY2
  572.     end
  573.  
  574.     local
  575.         screenIndex,
  576.         screenIndexStep,
  577.         pictureIndexStep,
  578.         background,
  579.         foreground,
  580.         alpha,
  581.         symbol = bufferWidth * (y - 1) + x, bufferWidth - clippedImageWidth, (imageWidth - clippedImageWidth) * 4
  582.  
  583.     for j = 1, clippedImageHeight do
  584.         for i = 1, clippedImageWidth do
  585.             alpha, symbol = picture[pictureIndex + 2], picture[pictureIndex + 3]
  586.            
  587.             -- If it's fully transparent pixel
  588.             if alpha == 0 then
  589.                 newFrameBackgrounds[screenIndex], newFrameForegrounds[screenIndex] = picture[pictureIndex], picture[pictureIndex + 1]
  590.             -- If it has some transparency
  591.             elseif alpha > 0 and alpha < 1 then
  592.                 newFrameBackgrounds[screenIndex] = color.blend(newFrameBackgrounds[screenIndex], picture[pictureIndex], alpha)
  593.                
  594.                 if blendForeground then
  595.                     newFrameForegrounds[screenIndex] = color.blend(newFrameForegrounds[screenIndex], picture[pictureIndex + 1], alpha)
  596.                 else
  597.                     newFrameForegrounds[screenIndex] = picture[pictureIndex + 1]
  598.                 end
  599.             -- If it's not transparent with whitespace
  600.             elseif symbol ~= " " then
  601.                 newFrameForegrounds[screenIndex] = picture[pictureIndex + 1]
  602.             end
  603.  
  604.             newFrameSymbols[screenIndex] = symbol
  605.  
  606.             screenIndex, pictureIndex = screenIndex + 1, pictureIndex + 4
  607.         end
  608.  
  609.         screenIndex, pictureIndex = screenIndex + screenIndexStep, pictureIndex + pictureIndexStep
  610.     end
  611. end
  612.  
  613. local function drawFrame(x, y, width, height, color)
  614.     local stringUp, stringDown, x2 = "┌" .. string.rep("─", width - 2) .. "┐", "└" .. string.rep("─", width - 2) .. "┘", x + width - 1
  615.    
  616.     drawText(x, y, color, stringUp); y = y + 1
  617.     for i = 1, height - 2 do
  618.         drawText(x, y, color, "│")
  619.         drawText(x2, y, color, "│")
  620.         y = y + 1
  621.     end
  622.     drawText(x, y, color, stringDown)
  623. end
  624.  
  625. --------------------------------------------------------------------------------
  626.  
  627. local function semiPixelRawSet(index, color, yPercentTwoEqualsZero)
  628.     local upperPixel, lowerPixel, bothPixel = "▀", "▄", " "
  629.     local background, foreground, symbol = newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index]
  630.  
  631.     if yPercentTwoEqualsZero then
  632.         if symbol == upperPixel then
  633.             if color == foreground then
  634.                 newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = color, foreground, bothPixel
  635.             else
  636.                 newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = color, foreground, symbol
  637.             end
  638.         elseif symbol == bothPixel then
  639.             if color ~= background then
  640.                 newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, color, lowerPixel
  641.             end
  642.         else
  643.             newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, color, lowerPixel
  644.         end
  645.     else
  646.         if symbol == lowerPixel then
  647.             if color == foreground then
  648.                 newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = color, foreground, bothPixel
  649.             else
  650.                 newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = color, foreground, symbol
  651.             end
  652.         elseif symbol == bothPixel then
  653.             if color ~= background then
  654.                 newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, color, upperPixel
  655.             end
  656.         else
  657.             newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, color, upperPixel
  658.         end
  659.     end
  660. end
  661.  
  662. local function semiPixelSet(x, y, color)
  663.     local yFixed = mathCeil(y / 2)
  664.     if x >= drawLimitX1 and yFixed >= drawLimitY1 and x <= drawLimitX2 and yFixed <= drawLimitY2 then
  665.         semiPixelRawSet(bufferWidth * (yFixed - 1) + x, color, y % 2 == 0)
  666.     end
  667. end
  668.  
  669. local function drawSemiPixelRectangle(x, y, width, height, color)
  670.     local index, evenYIndexStep, oddYIndexStep, realY, evenY =
  671.         bufferWidth * (mathCeil(y / 2) - 1) + x,
  672.         (bufferWidth - width),
  673.         width
  674.  
  675.     for pseudoY = y, y + height - 1 do
  676.         realY = mathCeil(pseudoY / 2)
  677.  
  678.         if realY >= drawLimitY1 and realY <= drawLimitY2 then
  679.             evenY = pseudoY % 2 == 0
  680.            
  681.             for pseudoX = x, x + width - 1 do
  682.                 if pseudoX >= drawLimitX1 and pseudoX <= drawLimitX2 then
  683.                     semiPixelRawSet(index, color, evenY)
  684.                 end
  685.  
  686.                 index = index + 1
  687.             end
  688.         else
  689.             index = index + width
  690.         end
  691.  
  692.         if evenY then
  693.             index = index + evenYIndexStep
  694.         else
  695.             index = index - oddYIndexStep
  696.         end
  697.     end
  698. end
  699.  
  700. local function drawSemiPixelLine(x1, y1, x2, y2, color)
  701.     rasterizeLine(x1, y1, x2, y2, function(x, y)
  702.         semiPixelSet(x, y, color)
  703.     end)
  704. end
  705.  
  706. local function drawSemiPixelEllipse(centerX, centerY, radiusX, radiusY, color)
  707.     rasterizeEllipse(centerX, centerY, radiusX, radiusY, function(x, y)
  708.         semiPixelSet(x, y, color)
  709.     end)
  710. end
  711.  
  712. --------------------------------------------------------------------------------
  713.  
  714. local function getPointTimedPosition(firstPoint, secondPoint, time)
  715.     return {
  716.         x = firstPoint.x + (secondPoint.x - firstPoint.x) * time,
  717.         y = firstPoint.y + (secondPoint.y - firstPoint.y) * time
  718.     }
  719. end
  720.  
  721. local function getConnectionPoints(points, time)
  722.     local connectionPoints = {}
  723.    
  724.     for point = 1, #points - 1 do
  725.         table.insert(connectionPoints, getPointTimedPosition(points[point], points[point + 1], time))
  726.     end
  727.  
  728.     return connectionPoints
  729. end
  730.  
  731. local function getMainPointPosition(points, time)
  732.     if #points > 1 then
  733.         return getMainPointPosition(getConnectionPoints(points, time), time)
  734.     else
  735.         return points[1]
  736.     end
  737. end
  738.  
  739. local function drawSemiPixelCurve(points, color, precision)
  740.     local linePoints = {}
  741.    
  742.     for time = 0, 1, precision or 0.01 do
  743.         table.insert(linePoints, getMainPointPosition(points, time))
  744.     end
  745.    
  746.     for point = 1, #linePoints - 1 do
  747.         drawSemiPixelLine(mathFloor(linePoints[point].x), mathFloor(linePoints[point].y), mathFloor(linePoints[point + 1].x), mathFloor(linePoints[point + 1].y), color)
  748.     end
  749. end
  750.  
  751. --------------------------------------------------------------------------------
  752.  
  753. local function update(force)
  754.     local index, indexStepOnEveryLine, changes = bufferWidth * (drawLimitY1 - 1) + drawLimitX1, (bufferWidth - drawLimitX2 + drawLimitX1 - 1), {}
  755.     local x, equalChars, equalCharsIndex, charX, charIndex, currentForeground
  756.     local currentFrameBackground, currentFrameForeground, currentFrameSymbol, changesCurrentFrameBackground, changesCurrentFrameBackgroundCurrentFrameForeground
  757.     local changesCurrentFrameBackgroundCurrentFrameForegroundIndex
  758.  
  759.     for y = drawLimitY1, drawLimitY2 do
  760.         x = drawLimitX1
  761.  
  762.         while x <= drawLimitX2 do
  763.             -- Determine if some pixel data was changed (or if <force> argument was passed)
  764.             if
  765.                 currentFrameBackgrounds[index] ~= newFrameBackgrounds[index] or
  766.                 currentFrameForegrounds[index] ~= newFrameForegrounds[index] or
  767.                 currentFrameSymbols[index] ~= newFrameSymbols[index] or
  768.                 force
  769.             then
  770.                 -- Make pixel at both frames equal
  771.                 currentFrameBackground, currentFrameForeground, currentFrameSymbol = newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index]
  772.                 currentFrameBackgrounds[index] = currentFrameBackground
  773.                 currentFrameForegrounds[index] = currentFrameForeground
  774.                 currentFrameSymbols[index] = currentFrameSymbol
  775.  
  776.                 -- Look for pixels with equal chars from right of current pixel
  777.                 equalChars, equalCharsIndex, charX, charIndex = {currentFrameSymbol}, 2, x + 1, index + 1
  778.                
  779.                 while charX <= drawLimitX2 do
  780.                     -- Pixels becomes equal only if they have same background and (whitespace char or same foreground)
  781.                     if 
  782.                         currentFrameBackground == newFrameBackgrounds[charIndex] and
  783.                         (
  784.                             newFrameSymbols[charIndex] == " " or
  785.                             currentFrameForeground == newFrameForegrounds[charIndex]
  786.                         )
  787.                     then
  788.                         -- Make pixel at both frames equal
  789.                         currentFrameBackgrounds[charIndex] = newFrameBackgrounds[charIndex]
  790.                         currentFrameForegrounds[charIndex] = newFrameForegrounds[charIndex]
  791.                         currentFrameSymbols[charIndex] = newFrameSymbols[charIndex]
  792.  
  793.                         equalChars[equalCharsIndex], equalCharsIndex = currentFrameSymbols[charIndex], equalCharsIndex + 1
  794.                     else
  795.                         break
  796.                     end
  797.  
  798.                     charX, charIndex = charX + 1, charIndex + 1
  799.                 end
  800.  
  801.                 -- Group pixels that need to be drawn by background and foreground
  802.                 changesCurrentFrameBackground = changes[currentFrameBackground] or {}
  803.                 changes[currentFrameBackground] = changesCurrentFrameBackground
  804.                 changesCurrentFrameBackgroundCurrentFrameForeground = changesCurrentFrameBackground[currentFrameForeground] or {index = 1}
  805.                 changesCurrentFrameBackground[currentFrameForeground] = changesCurrentFrameBackgroundCurrentFrameForeground
  806.  
  807.                 changesCurrentFrameBackgroundCurrentFrameForegroundIndex = changesCurrentFrameBackgroundCurrentFrameForeground.index
  808.                 changesCurrentFrameBackgroundCurrentFrameForeground[changesCurrentFrameBackgroundCurrentFrameForegroundIndex], changesCurrentFrameBackgroundCurrentFrameForegroundIndex = x, changesCurrentFrameBackgroundCurrentFrameForegroundIndex + 1
  809.                 changesCurrentFrameBackgroundCurrentFrameForeground[changesCurrentFrameBackgroundCurrentFrameForegroundIndex], changesCurrentFrameBackgroundCurrentFrameForegroundIndex = y, changesCurrentFrameBackgroundCurrentFrameForegroundIndex + 1
  810.                 changesCurrentFrameBackgroundCurrentFrameForeground[changesCurrentFrameBackgroundCurrentFrameForegroundIndex], changesCurrentFrameBackgroundCurrentFrameForegroundIndex = table.concat(equalChars), changesCurrentFrameBackgroundCurrentFrameForegroundIndex + 1
  811.                
  812.                 x, index, changesCurrentFrameBackgroundCurrentFrameForeground.index = x + equalCharsIndex - 2, index + equalCharsIndex - 2, changesCurrentFrameBackgroundCurrentFrameForegroundIndex
  813.             end
  814.  
  815.             x, index = x + 1, index + 1
  816.         end
  817.  
  818.         index = index + indexStepOnEveryLine
  819.     end
  820.    
  821.     -- Draw grouped pixels on screen
  822.     for background, foregrounds in pairs(changes) do
  823.         componentInvoke(GPUAddress, "setBackground", background)
  824.  
  825.         for foreground, pixels in pairs(foregrounds) do
  826.             if currentForeground ~= foreground then
  827.                 componentInvoke(GPUAddress, "setForeground", foreground)
  828.                 currentForeground = foreground
  829.             end
  830.  
  831.             for i = 1, #pixels, 3 do
  832.                 componentInvoke(GPUAddress, "set", pixels[i], pixels[i + 1], pixels[i + 2])
  833.             end
  834.         end
  835.     end
  836.  
  837.     changes = nil
  838. end
  839.  
  840. -- Grapes: Setting up da screen library
  841.  
  842. setGPUAddress(component.list("gpu")())
  843.  
  844. if not component.invoke(component.list("gpu")(), "getScreen") then
  845.     local address = component.list("screen")()
  846.    
  847.     if address then
  848.         setScreenAddress(address, false)
  849.     end
  850. else
  851.     setScreenAddress(component.invoke(component.list("gpu")(), "getScreen"), false)
  852. end
  853.  
  854. --------------------------------------------------------------------------------
  855.  
  856. return {
  857.     getIndex = getIndex,
  858.     setDrawLimit = setDrawLimit,
  859.     resetDrawLimit = resetDrawLimit,
  860.     getDrawLimit = getDrawLimit,
  861.     flush = flush,
  862.    
  863.     setResolution = setResolution,
  864.     getMaxResolution = getMaxResolution,
  865.  
  866.     setGPUAddress = setGPUAddress,
  867.     getGPUAddress = getGPUAddress,
  868.     setScreenAddress = setScreenAddress,
  869.    
  870.     getColorDepth = getColorDepth,
  871.     setColorDepth = setColorDepth,
  872.     getMaxColorDepth = getMaxColorDepth,
  873.  
  874.     getScaledResolution = getScaledResolution,
  875.     getResolution = getResolution,
  876.     getWidth = getWidth,
  877.     getHeight = getHeight,
  878.     getCurrentFrameTables = getCurrentFrameTables,
  879.     getNewFrameTables = getNewFrameTables,
  880.  
  881.     getScreenAspectRatio = getScreenAspectRatio,
  882.     getScreenAddress = getScreenAddress,
  883.  
  884.     rawSet = rawSet,
  885.     rawGet = rawGet,
  886.     get = get,
  887.     set = set,
  888.     clear = clear,
  889.     copy = copy,
  890.     paste = paste,
  891.     rasterizeLine = rasterizeLine,
  892.     rasterizeEllipse = rasterizeEllipse,
  893.     rasterizePolygon = rasterizePolygon,
  894.     semiPixelRawSet = semiPixelRawSet,
  895.     semiPixelSet = semiPixelSet,
  896.     update = update,
  897.  
  898.     drawRectangle = drawRectangle,
  899.     drawLine = drawLine,
  900.     drawEllipse = drawEllipse,
  901.     drawPolygon = drawPolygon,
  902.     drawText = drawText,
  903.     drawImage = drawImage,
  904.     drawFrame = drawFrame,
  905.     blur = blur,
  906.  
  907.     drawSemiPixelRectangle = drawSemiPixelRectangle,
  908.     drawSemiPixelLine = drawSemiPixelLine,
  909.     drawSemiPixelEllipse = drawSemiPixelEllipse,
  910.     drawSemiPixelCurve = drawSemiPixelCurve,
  911. }
  912.  
Tags: ORMS
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement