Light83

GfxBuffer Lua (OpenComputer LanteaCraft)

Apr 19th, 2017
310
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.08 KB | None | 0 0
  1. local component=require("component")
  2. local unicode=require("unicode")
  3.  
  4. local colorutils=require("colorutils")
  5.  
  6. local buffer={VERSION="1.0"}
  7. local bufferMeta={}
  8.  
  9. local debugPrint=function() end
  10.  
  11. --copy these to file local, they're called a lot in performance-intensive loops
  12. local convColor_hto8=colorutils.convColor_hto8
  13. local convColor_8toh=colorutils.convColor_8toh
  14.  
  15.  
  16. local function encodeColor(fg,bg)
  17. return convColor_hto8(bg)*256+convColor_hto8(fg)
  18. end
  19.  
  20. local function decodeColor(c)
  21. return convColor_8toh(math.floor(c/256)),convColor_8toh(c%256)
  22. end
  23.  
  24.  
  25.  
  26. function bufferMeta.getBackground(buffer)
  27. return convColor_8toh(buffer.colorBackground)
  28. end
  29.  
  30. function bufferMeta.setBackground(buffer,color)
  31. local p=buffer.colorBackground
  32. buffer.colorBackground=color
  33. buffer.color=encodeColor(buffer.colorForeground,color)
  34. return p
  35. end
  36.  
  37. function bufferMeta.getForeground(buffer)
  38. return buffer.colorForeground
  39. end
  40.  
  41. function bufferMeta.setForeground(buffer,color)
  42. local p=buffer.colorForeground
  43. buffer.colorForeground=color
  44. buffer.color=encodeColor(color,buffer.colorBackground)
  45. return p
  46. end
  47.  
  48.  
  49. function bufferMeta.copy(buffer,x,y,w,h,dx,dy)
  50. buffer.flush()
  51. return buffer.parent.copy(x,y,w,h,dx,dy)
  52. end
  53.  
  54. function bufferMeta.fill(buffer,x,y,w,h,char)
  55. buffer.flush()
  56. buffer.parent.setForeground(buffer.colorForeground)
  57. buffer.parent.setBackground(buffer.colorBackground)
  58. return buffer.parent.fill(x,y,w,h,char)
  59. end
  60.  
  61. function bufferMeta.get(buffer,x,y)
  62. buffer.flush()
  63. return buffer.parent.get(x,y)
  64. end
  65.  
  66. function bufferMeta.set(buffer,x,y,str)
  67. local spans=buffer.spans
  68.  
  69. local spanI=1
  70. local color=buffer.color
  71. local e=x+unicode.len(str)-1
  72.  
  73. while spans[spanI] and (spans[spanI].y<y or spans[spanI].y==y and spans[spanI].e<x) do
  74. spanI=spanI+1
  75. end
  76. --ok, now spanI is either intersecting me or the first after me
  77. --if intersect, crop
  78.  
  79. if not spans[spanI] then
  80. debugPrint("just inserting at "..spanI)
  81. local span={str=str,e=e,x=x,y=y,color=color}
  82. spans[spanI]=span
  83. else
  84. local span=spans[spanI]
  85. debugPrint("scanned to span "..spanI)
  86. if span.y==y and span.x<e then
  87. debugPrint("it starts before I end.")
  88. --it starts before me. Can I merge with it?
  89. if span.color==color then
  90. --we can merge. Yay.
  91. --splice myself in
  92. debugPrint("splicing at "..math.max(0,(x-span.x)))
  93. local a,c=unicode.sub(span.str,1,math.max(0,x-span.x)), unicode.sub(span.str,e-span.x+2)
  94. debugPrint("before=\""..a.."\", after=\""..c..'"')
  95. span.str=a..str..c
  96. --correct x and e(nd)
  97. if x<span.x then
  98. span.x=x
  99. end
  100. if e > span.e then
  101. span.e=e
  102. end
  103. else
  104. --can't, gonna have to make a new span
  105. --but first, split this guy as needed
  106. debugPrint("can't merge. Splitting")
  107. local a,b=unicode.sub(span.str,1,math.max(0,x-span.x)),unicode.sub(span.str,e-span.x+2)
  108. if unicode.len(a)>0 then
  109. span.str=a
  110. span.e=span.x+unicode.len(a)
  111. --span is a new span
  112. span={str=true,e=true,x=true,y=y,color=span.color}
  113. --insert after this span
  114. spanI=spanI+1
  115. table.insert(spans,spanI,span)
  116. end
  117. if unicode.len(b)>0 then
  118. span.str=b
  119. span.x=e+1
  120. span.e=span.x+unicode.len(b)
  121.  
  122. --and another new span
  123. span={str=true,e=true,x=true,y=y,color=color}
  124. --insert /before/ this one
  125. table.insert(spans,spanI,span)
  126. end
  127. --now make whatever span we're left with me.
  128. span.color=color
  129. span.x, span.e = x, e
  130. span.str=str
  131. span.y=y
  132. end
  133. else
  134. --starts after me. just insert.
  135. local span={x=x,e=e,y=y,color=color,str=str}
  136. table.insert(spans,spanI,span)
  137. end
  138. --ok. We are span. We are at spanI. We've inserted ourselves. Now just check if we've obliterated anyone.
  139. --while the next span starts before I end...
  140. spanI=spanI+1
  141. while spans[spanI] and spans[spanI].y==y and spans[spanI].x<=e do
  142. local span=spans[spanI]
  143. if span.e>e then
  144. --it goes past me, we just circumcise it
  145. span.str=unicode.sub(span.str,e-span.x+2)
  146. span.x=e+1
  147. break--and there can't be more
  148. end
  149. --doesn't end after us, means we obliterated it
  150. table.remove(spans,spanI)
  151. --spanI will now point to the next, if any
  152. end
  153. end
  154.  
  155. --[[this..won't work. Was forgetting I have a table per row, this would count rows.
  156. if #spans>=buffer.autoFlushCount then
  157. buffer.flush()
  158. end
  159. --]]
  160. end
  161.  
  162.  
  163. function bufferMeta.flush(buffer)
  164. debugPrint("flush?")
  165. if #buffer.spans==0 then
  166. return
  167. end
  168.  
  169. --sort by colors. bg is added as high value, so this will group all with common bg together,
  170. --and all with common fg together within same bg.
  171. table.sort(buffer.spans,
  172. function(spanA,spanB)
  173. if spanA.color==spanB.color then
  174. if spanA.y==spanB.y then
  175. return spanA.x<spanB.x
  176. end
  177. return spanA.y<spanB.y
  178. end
  179. return spanA.color<spanB.color
  180. end )
  181.  
  182. --now draw the spans!
  183. local parent=buffer.parent
  184. local cfg,cbg=pfg,pbg
  185. local spans=buffer.spans
  186.  
  187. for i=1,#spans do
  188. local span=spans[i]
  189. local bg,fg=decodeColor(span.color)
  190. if fg~=cfg then
  191. parent.setForeground(fg)
  192. cfg=fg
  193. end
  194. if bg~=cbg then
  195. parent.setBackground(bg)
  196. cbg=bg
  197. end
  198. parent.set(span.x,span.y,span.str)
  199. end
  200. if cfg~=buffer.colorForeground then
  201. parent.setForeground(buffer.colorForeground)
  202. end
  203. if cbg~=buffer.colorBackground then
  204. parent.setBackground(buffer.colorBackground)
  205. end
  206. --...and that's that. Throw away our spans.
  207. buffer.spans={}
  208. --might have to experiment later, see if the cost of rebuilding (and re-growing) the table is offset
  209. --by the savings of not having the underlying spans object grow based on peak buffer usage,
  210. --but if I'm optimizing for memory (and I am, in this case), then this seems a safe call for now.
  211. --If it ends up an issue, might be able to offset the computational cost by initing to an array of some average size, then
  212. --niling the elements in a loop.
  213.  
  214. end
  215.  
  216. function buffer.create(parent)
  217. parent=parent or component.gpu
  218. local width,height=parent.getResolution()
  219.  
  220. local newBuffer={
  221. colorForeground=0xffffff,
  222. colorBackground=0x000000,
  223. color=0x00ff,
  224. width=width,
  225. height=height,
  226. parent=parent,
  227. spans={},
  228. autoFlushCount=32,
  229. getResolution=parent.getResolution,
  230. setResolution=parent.setResolution,
  231. maxResolution=parent.maxResolution,
  232. getDepth=parent.getDepth,
  233. setDepth=parent.setDepth,
  234. maxDepth=parent.maxDepth,
  235. getSize=parent.getSize,
  236. }
  237.  
  238. setmetatable(newBuffer,{__index=function(tbl,key) local v=bufferMeta[key] if type(v)=="function" then return function(...) return v(tbl,...) end end return v end})
  239.  
  240. return newBuffer
  241. end
  242.  
  243.  
  244. return buffer
Add Comment
Please, Sign In to add comment