Advertisement
Schneemann

dsd

Sep 15th, 2021
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.44 KB | None | 0 0
  1. Skip to content
  2. Why GitHub?
  3. Team
  4. Enterprise
  5. Explore
  6. Marketplace
  7. Pricing
  8. Search
  9. Sign in
  10. Sign up
  11. ChenThread
  12. /
  13. octagon
  14. Public
  15. 83
  16. Code
  17. Issues
  18. 3
  19. Pull requests
  20. Actions
  21. Projects
  22. Wiki
  23. Security
  24. Insights
  25. octagon/pngview.lua
  26. @asiekierka
  27. asiekierka add pngview
  28. Latest commit c9d8783 on Jun 30, 2018
  29. History
  30. 1 contributor
  31. 322 lines (288 sloc) 8.63 KB
  32.  
  33. --[[
  34. pngview: Simple high-resolution PNG viewer
  35. Copyright (c) 2016, 2017, 2018 asie
  36. Permission is hereby granted, free of charge, to any person obtaining a copy of
  37. this software and associated documentation files (the "Software"), to deal in
  38. the Software without restriction, including without limitation the rights to
  39. use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  40. of the Software, and to permit persons to whom the Software is furnished to do
  41. so, subject to the following conditions:
  42. The above copyright notice and this permission notice shall be included in all
  43. copies or substantial portions of the Software.
  44. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  45. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  46. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  47. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  48. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  49. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  50. SOFTWARE.
  51. ]]
  52.  
  53. local args = {...}
  54. local ocpng = require("png")
  55. local component = require("component")
  56. local event = require("event")
  57. local gpu = component.gpu
  58. local unicode = require("unicode")
  59. local keyboard = require("keyboard")
  60. local text = require("text")
  61. local os = require("os")
  62. local pal = {}
  63.  
  64. -- 4x4 dithermask
  65. --local oDither = {
  66. -- 0, 8, 2, 10,
  67. -- 12, 4, 14, 6,
  68. -- 3, 11, 1, 9,
  69. -- 15, 7, 13, 5
  70. --}
  71. -- 2x2 dithermask
  72. local oDither = {
  73. 0, 2,
  74. 3, 1
  75. }
  76. local oDSize = #oDither
  77. local oDWidth = math.floor(math.sqrt(#oDither))
  78.  
  79. local q = {}
  80. for i=0,255 do
  81. local dat = (i & 0x01) << 7
  82. dat = dat | (i & 0x02) >> 1 << 6
  83. dat = dat | (i & 0x04) >> 2 << 5
  84. dat = dat | (i & 0x08) >> 3 << 2
  85. dat = dat | (i & 0x10) >> 4 << 4
  86. dat = dat | (i & 0x20) >> 5 << 1
  87. dat = dat | (i & 0x40) >> 6 << 3
  88. dat = dat | (i & 0x80) >> 7
  89. q[i + 1] = unicode.char(0x2800 | dat)
  90. end
  91.  
  92. local function round(v)
  93. return math.floor(v + 0.5)
  94. end
  95.  
  96. function resetPalette(data)
  97. for i=0,255 do
  98. if (i < 16) then
  99. -- if data == nil or data[3] == nil or data[3][i] == nil then
  100. pal[i] = (i * 15) << 16 | (i * 15) << 8 | (i * 15)
  101. -- else
  102. -- pal[i] = data[3][i]
  103. -- gpu.setPaletteColor(i, data[3][i])
  104. -- end
  105. else
  106. local j = i - 16
  107. local b = math.floor((j % 5) * 255 / 4.0)
  108. local g = math.floor((math.floor(j / 5.0) % 8) * 255 / 7.0)
  109. local r = math.floor((math.floor(j / 40.0) % 6) * 255 / 5.0)
  110. pal[i] = r << 16 | g << 8 | b
  111. end
  112. end
  113. end
  114.  
  115. resetPalette(nil)
  116.  
  117. local function pngSafeGet(png, x, y, paletted)
  118. if x >= png.w then x = png.w - 1 elseif x < 0 then x = 0 end
  119. if y >= png.h then y = png.h - 1 elseif y < 0 then y = 0 end
  120. return png:get(x, y, paletted)
  121. end
  122.  
  123. local function maxAbs(r, g, b)
  124. local d1 = r - g
  125. local d2 = g - b
  126. if d1 < 0 then d1 = -d1 end
  127. if d2 < 0 then d2 = -d2 end
  128. if d1 >= d2 then return d1 else return d2 end
  129. end
  130.  
  131. local function getOCPalEntry(png, data, rgb)
  132. -- TODO: more cleverness
  133. local r = (rgb >> 16) & 0xFF
  134. local g = (rgb >> 8) & 0xFF
  135. local b = (rgb) & 0xFF
  136. if maxAbs(r, g, b) <= 6 then
  137. local i = round(g * 16.0 / 255.0)
  138. if i <= 0 then return 16 elseif i >= 16 then return 255 else return i - 1 end
  139. else
  140. return 16 + (round(r * 5.0 / 255.0) * 40) + (round(g * 7.0 / 255.0) * 5) + round(b * 4.0 / 255.0)
  141. end
  142. end
  143.  
  144. local function colorDistSq(rgb1, rgb2)
  145. if rgb1 == rgb2 then return 0 end
  146.  
  147. local r1 = (rgb1 >> 16) & 0xFF
  148. local g1 = (rgb1 >> 8) & 0xFF
  149. local b1 = (rgb1) & 0xFF
  150. local r2 = (rgb2 >> 16) & 0xFF
  151. local g2 = (rgb2 >> 8) & 0xFF
  152. local b2 = (rgb2) & 0xFF
  153. local rs = (r1 - r2) * (r1 - r2)
  154. local gs = (g1 - g2) * (g1 - g2)
  155. local bs = (b1 - b2) * (b1 - b2)
  156. local rAvg = math.floor((r1 + r2) / 2)
  157.  
  158. return (((512 + rAvg) * rs) >> 8) + (4 * gs) + (((767 - rAvg) * bs) >> 8)
  159. end
  160.  
  161. -- inspired by the teachings of Bisqwit
  162. -- http://bisqwit.iki.fi/story/howto/dither/jy/
  163. local function ditherDistance(rgb, rgb1, rgb2)
  164. if rgb1 == rgb2 then return 0.5 end
  165.  
  166. local r = (rgb >> 16) & 0xFF
  167. local g = (rgb >> 8) & 0xFF
  168. local b = (rgb) & 0xFF
  169. local r1 = (rgb1 >> 16) & 0xFF
  170. local g1 = (rgb1 >> 8) & 0xFF
  171. local b1 = (rgb1) & 0xFF
  172. local r2 = (rgb2 >> 16) & 0xFF
  173. local g2 = (rgb2 >> 8) & 0xFF
  174. local b2 = (rgb2) & 0xFF
  175.  
  176. return (r * r1 - r * r2 - r1 * r2 + r2 * r2 +
  177. g * g1 - g * g2 - g1 * g2 + g2 * g2 +
  178. b * b1 - b * b2 - b1 * b2 + b2 * b2) /
  179. ((r1 - r2) * (r1 - r2) +
  180. (g1 - g2) * (g1 - g2) +
  181. (b1 - b2) * (b1 - b2));
  182. end
  183.  
  184. function loadImage(filename)
  185. local png = ocpng.loadPNG(filename)
  186.  
  187. local charH = math.ceil(png.h / 4.0)
  188. local charW = math.ceil(png.w / 2.0)
  189. local data = {}
  190.  
  191. data[1] = {}
  192. data[2] = {charW, charH}
  193. data[3] = {}
  194.  
  195. -- todo: populate with customColors [3][0]...
  196.  
  197. local j = 1
  198. for y=0,charH-1 do
  199. for x=0,charW-1 do
  200. local pixelsRGB = {
  201. pngSafeGet(png, x*2+1, y*4+3),
  202. pngSafeGet(png, x*2, y*4+3),
  203. pngSafeGet(png, x*2+1, y*4+2),
  204. pngSafeGet(png, x*2, y*4+2),
  205. pngSafeGet(png, x*2+1, y*4+1),
  206. pngSafeGet(png, x*2, y*4+1),
  207. pngSafeGet(png, x*2+1, y*4),
  208. pngSafeGet(png, x*2, y*4)
  209. }
  210. local pixels = {}
  211. for i=1,#pixelsRGB do pixels[i] = getOCPalEntry(png, data, pixelsRGB[i]) end
  212. -- identify most common colors
  213. local pixelCount = {}
  214. local pixelColors = 0
  215. for i=1,#pixels do
  216. if pixelCount[pixels[i]] == nil then pixelColors = pixelColors + 1 end
  217. pixelCount[pixels[i]] = (pixelCount[pixels[i]] or 0) + 1
  218. end
  219. if pixelColors == 1 then
  220. data[1][j] = (0 << 16) | (0 << 8) | pixels[1]
  221. else
  222. local bg = -1
  223. local bgc = -1
  224. local fg = -1
  225. local fgc = -1
  226. for k,v in pairs(pixelCount) do
  227. if v > bgc then
  228. bg = k
  229. bgc = v
  230. end
  231. end
  232. for k,v in pairs(pixelCount) do
  233. local contrast = colorDistSq(pal[bg], pal[k]) * v
  234. if k ~= bg and contrast > fgc then
  235. fg = k
  236. fgc = contrast
  237. end
  238. end
  239. local chr = 0
  240. for i=1,#pixels do
  241. local px = x * 2 + ((i - 1) & 1)
  242. local py = y * 4 + ((i - 1) >> 1)
  243. local dDist = ditherDistance(pixelsRGB[i], pal[bg], pal[fg])
  244. local dDi = oDSize - round(dDist * oDSize)
  245. local dThr = oDither[1 + ((py % oDWidth) * oDWidth) + (px % oDWidth)]
  246. if dThr < dDi then
  247. chr = chr | (1 << (i - 1))
  248. end
  249. end
  250. data[1][j] = bg | (fg << 8) | (chr << 16)
  251. end
  252. j = j + 1
  253. end
  254. end
  255.  
  256. return data
  257. end
  258.  
  259. function gpuBG()
  260. local a, al = gpu.getBackground()
  261. if al then
  262. return gpu.getPaletteColor(a)
  263. else
  264. return a
  265. end
  266. end
  267. function gpuFG()
  268. local a, al = gpu.getForeground()
  269. if al then
  270. return gpu.getPaletteColor(a)
  271. else
  272. return a
  273. end
  274. end
  275.  
  276. function drawImage(data, offx, offy)
  277. if offx == nil then offx = 0 end
  278. if offy == nil then offy = 0 end
  279.  
  280. local WIDTH = data[2][1]
  281. local HEIGHT = data[2][2]
  282.  
  283. gpu.setResolution(WIDTH, HEIGHT)
  284. resetPalette(data)
  285.  
  286. local bg = 0
  287. local fg = 0
  288. local cw = 1
  289. local noBG = false
  290. local noFG = false
  291. local ind = 1
  292.  
  293. local gBG = gpuBG()
  294. local gFG = gpuFG()
  295.  
  296. for y=0,HEIGHT-1 do
  297. local str = ""
  298. for x=0,WIDTH-1 do
  299. ind = (y * WIDTH) + x + 1
  300. bg = pal[data[1][ind] & 0xFF]
  301. fg = pal[(data[1][ind] >> 8) & 0xFF]
  302. cw = ((data[1][ind] >> 16) & 0xFF) + 1
  303. noBG = (cw == 256)
  304. noFG = (cw == 1)
  305. if (noFG or (gBG == fg)) and (noBG or (gFG == bg)) then
  306. str = str .. q[257 - cw]
  307. -- str = str .. "I"
  308. elseif (noBG or (gBG == bg)) and (noFG or (gFG == fg)) then
  309. str = str .. q[cw]
  310. else
  311. if #str > 0 then
  312. gpu.set(x + 1 + offx - unicode.wlen(str), y + 1 + offy, str)
  313. end
  314. if (gBG == fg and gFG ~= bg) or (gFG == bg and gBG ~= fg) then
  315. cw = 257 - cw
  316. local t = bg
  317. bg = fg
  318. fg = t
  319. end
  320. if gBG ~= bg then
  321. gpu.setBackground(bg)
  322. gBG = bg
  323. end
  324. if gFG ~= fg then
  325. gpu.setForeground(fg)
  326. gFG = fg
  327. end
  328. str = q[cw]
  329. -- if (not noBG) and (not noFG) then str = "C" elseif (not noBG) then str = "B" elseif (not noFG) then str = "F" else str = "c" end
  330. end
  331. end
  332. if #str > 0 then
  333. gpu.set(WIDTH + 1 - unicode.wlen(str) + offx, y + 1 + offy, str)
  334. end
  335. end
  336. end
  337.  
  338. local image = loadImage(args[1])
  339. drawImage(image)
  340.  
  341. while true do
  342. local name,addr,char,key,player = event.pull("key_down")
  343. if key == 0x10 then
  344. break
  345. end
  346. end
  347.  
  348. gpu.setBackground(0, false)
  349. gpu.setForeground(16777215, false)
  350. gpu.setResolution(80, 25)
  351. gpu.fill(1, 1, 80, 25, " ")
  352. © 2021 GitHub, Inc.
  353. Terms
  354. Privacy
  355. Security
  356. Status
  357. Docs
  358. Contact GitHub
  359. Pricing
  360. API
  361. Training
  362. Blog
  363. About
  364. Loading complete
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement