Created
May 20, 2013 01:37
-
-
Save dermotbalson/5609902 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# Notes | |
--# ReadMe | |
--[[ | |
First, don't be put off by all this text. This utility is extremely simple to use. | |
It creates code that reproduces images so you don't need to include them separately with your projects. It uses RLE, a well known compression technique. Essentially, it runs through all the pixels, and every time the color changes, it stores the last color and the number of pixels it applied to. | |
FUNCTIONALITY | |
It can turn any image(with reason) into code strings, and reassemble the image from them. | |
It can take images from the libraries, or Spritely code, and convert to compressed code. | |
The practical limits on image size and complexity are processing speed and storage, because the greater the number of colors and pixels, the more space we have to allow for each item. | |
FUNCTIONS | |
The Coder:Encode function takes the image as input and prints the code to recreate it. You copy this code to your project. The Test tab shows how it looks. Note, you will also need to copy the Decoder tab to your project. | |
Usage: Coder:Encode(img) --then copy and paste the printed code | |
NB As well as printing the code, the Coder function also returns the two strings required to decode the image. This is only for testing purposes, so the Main tab can show how it works - in practice, you will just copy the printed code and not worry about the return values. | |
The Decoder:Decode function takes two strings as input and returns an image. Normally, you don't have to worry about this because the printed code calls the function correctly for you, returning the image. | |
The Main function carries out several tests | |
It reads in an image from the standard library, codes and then decodes it back to an image | |
It takes an image previously created by this code, and encodes/decodes it back to an image | |
USING IT | |
Only the ImageCoder tab is needed to encode images | |
Only the Decoder tab needs to be included in any project where you want to decode RLE images. | |
COMPRESSION | |
The level of compression depends on many factors, but the code generated by this project seems to e not much greater than the original image size. | |
Dermot (user:Ignatz) | |
--]] | |
--# Main | |
--# Main | |
-- Compress | |
function setup() | |
--Read image from library | |
--You can pick any image you like, but don't make it too big or complex | |
img1 = readImage("Space Art:Part Red Hull 1") | |
saveImage("Dropbox:Cute",img1) | |
--Normally, you only need to run the single line of code below, which prints the code you need | |
--The function does return two strings, but you can ignore them (they are just for testing), | |
--just copy what is printed out in the Output screen, and and paste it into your own project | |
--So normally, you'll just run Coder:Encode(img1) | |
--The Test tab shows how it looks when pasted in, and that is the third example below | |
local strCol,str=Coder:Encode(img1) | |
--The rest of the code below is just to prove it works | |
--check we can reproduce the original image we loaded above, using the compressed code | |
img_new1=Decoder:Decode(img1.width,img1.height,strCol,str) | |
--print(string.len(strCol..str)) --uncomment this to see the size of the printed code | |
img2=Test:Decode() | |
end | |
-- This function gets called once every frame | |
function draw() | |
-- This sets a dark background color | |
background(208, 208, 214, 255) | |
-- This sets the line thickness | |
strokeWidth(5) | |
-- Do your drawing here | |
text( "This utility converts images to code you can embed in your project", 290, 550 ) | |
text( "Library original", 100, 500 ) | |
sprite(img1,100,400) | |
text( "Copy from code", 100, 250 ) | |
sprite(img_new1,100,150) | |
if img~=nil then sprite(img2,300,150) end | |
end | |
--# Coder | |
--# Coder | |
Coder = class() | |
--This function encodes an image in two strings | |
--the first string makes a list of unique color settings (r,g,b,a), encoded as an 8 char hex string | |
--the second string runs through the image, through rows then columns, and every time the color changes, | |
--it records the color setting and number of cells that had that color | |
--NB it doesn't store the color setting as an 8 char hex string, imstead it stores the position of | |
--this color in the first string. This saves a lot of storage where colors are used over and over again | |
Codes="!#&~*+,)/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ]_`abcdefghijklmnopqrstuvwxyz" | |
-- 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 | |
-- 1 2 3 4 5 6 7 8 | |
function Coder:Encode(img) | |
local rows=img.height | |
local cols=img.width | |
local colors={} --hash table of unique colors, gives us their sequence in the unique list | |
local strCol="" --unique list of color settings, each one is 8 chars hex, for r,g,b,a | |
local str="" -- each item consists of a color setting (or rather, the position in strCol), and the number of | |
--cells to be colored | |
--for example, if the image starts with 56 cells with r=1,g=2,b=5,a=6, we will add the hex value of this, | |
--which is 001002005006, to strCol, and set colors[001002005006]=1, (1 because this is the first color) | |
--to str, we add the hex values of the string position, ie 1, and the number of cells, ie 56, so we will | |
--add 001038 to str | |
local colorCount=0 | |
local prevColor=-1 --to tell us when the color changes | |
local count=0 --number of cells with current color | |
local r,g,b,a,x | |
local tbl={} | |
local tblCol={} | |
--loop through image | |
for i=1,cols do | |
for j=1,rows do | |
r,g,b,a=img:get(i,j) | |
x=r..","..g..","..b..","..a | |
if x==prevColor then | |
count=count+1 | |
else --color has changed, store details of last color | |
if count>0 then --this will only be zero for the very first cell | |
y=colors[prevColor] --this looks up the position of the color, in the color list | |
if y==nil then --add the color to the list if not there | |
colorCount = colorCount + 1 | |
y=colorCount | |
colors[prevColor]=colorCount | |
--if colorCount>1 then | |
--table.insert(tblCol,","..prevColor) | |
--else | |
table.insert(tblCol,prevColor) | |
--end | |
end | |
table.insert(tbl,y..","..count) | |
end | |
prevColor=x | |
count=1 | |
end | |
if errMessage~=nil then break end | |
end | |
if errMessage~=nil then break end | |
end | |
--we've finished, but we may have some left over chars that need to be stored | |
if count>0 then | |
y=colors[prevColor] | |
if y==nil then | |
colorCount = colorCount + 1 | |
colors[prevColor]=colorCount | |
y=colorCount | |
end | |
table.insert(tblCol,y) | |
table.insert(tbl,count) | |
end | |
str=table.concat(tbl,",") | |
strCol=table.concat(tblCol,",") | |
--create code for user | |
if errMessage~=nil then | |
print(errMessage) | |
return errMessage,errMessage | |
else | |
str1=Coder:Compress(strCol) | |
str2=Coder:Compress(str) | |
Coder:PrintCode(cols,rows,str1,str2) | |
return str1,str2 | |
end | |
end | |
function Coder:Compress(d) | |
local a={} | |
local m=0 | |
local y,z | |
local x=string.gsub(d,"8","82") | |
x=string.gsub(x,"7","81") | |
x=string.gsub(x,"9","83") | |
x=string.gsub(x,",","7") | |
for i=1,#x,2 do | |
y=string.sub(x,i,i)*9 | |
if i<#x then y=y+string.sub(x,i+1,i+1) else y=y+7 end | |
y=y+1 | |
z=string.sub(Codes,y,y) | |
table.insert(a,z) | |
end | |
return table.concat(a) | |
end | |
function Coder:PrintCode(c,r,s1,s2) | |
print("img=Decoder:Decode("..c..","..r..",'"..s1.."','"..s2.."')") | |
end | |
--# Decoder | |
Decoder=class() | |
Codes="!#&~*+,)/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ]_`abcdefghijklmnopqrstuvwxyz" | |
-- 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 | |
-- 1 2 3 4 5 6 7 8 | |
function Decoder:Decode(cols,rows,cc,dd) | |
local img=image(cols,rows) | |
local n=0 | |
local m=4 | |
local arrCols={} | |
strCol=Decoder:Decompress(cc) | |
strDat=Decoder:Decompress(dd) | |
--unpack color descriptions | |
for q in string.gmatch(strCol,"[^,]+") do | |
m = m + 1 | |
if m==5 then | |
m=1 | |
n = n + 1 | |
arrCols[n]={} | |
end | |
arrCols[n][m]=q | |
end | |
--if m~=4 then print("faulty column codes, m=",m) end | |
--unpack RLE content | |
local col=1 | |
local row=0 | |
local ind=1 | |
n=0 | |
for q in string.gmatch(strDat, "[^,]+") do | |
if ind==1 then | |
colIndex=tonumber(q) | |
else | |
colCount=tonumber(q) | |
n = n + 1 | |
for u=1,colCount do | |
row = row + 1 | |
if row>rows then col=col+1 row=1 end | |
img:set(col,row, | |
color(arrCols[colIndex][1],arrCols[colIndex][2],arrCols[colIndex][3],arrCols[colIndex][4])) | |
end | |
end | |
ind=3-ind | |
end | |
return img | |
end | |
function Decoder:Decompress(a) | |
local s={} | |
local d1,d2,y,x | |
for i=1,#a do | |
y=string.find(Codes,string.sub(a,i,i))-1 | |
d2=y%9 | |
d1=(y-d2)/9 | |
d2=d2 | |
table.insert(s,d1) | |
table.insert(s,d2) | |
end | |
if s[#s]==7 then table.remove(s,#s) end | |
str=table.concat(s) | |
x=string.gsub(str,"7",",") | |
x=string.gsub(x,"81","7") | |
x=string.gsub(x,"83","9") | |
x=string.gsub(x,"82","8") | |
return x | |
end | |
--# Test | |
--# Test | |
Test=class() | |
function Test:Decode() | |
--code produced by | |
img=Decoder:Decode(48,76,'))))ck7:j1lR1j7`j#lIEjsg4smumukYqAIAIAI5gulljg5un@VkHjDmROk=jJ@Qmg=)=q@tmI_I8I8I0I4]S@S@>IKjI3q0js@_7_7>]2@Km)8Bl@0j)Zl70j)Yj>m7Lk/@2um@MjugtukukujYjVn)Tk?jJ7Pm]<]2mmk@3]Pm]<@67WnI=)37NmI;I3ROmR<I6nnkuj;m)Kju7@))R4RS@S@>)27Km)8BoI:k71)4IS7S7:R4@S7S7=g1@J7J78GkiinjPmtmtkVjs)Zng>I81ngZkXj<m)Kk#jOmtmtkUlsj@2o]6)WnI<tjA@Mm@8Gq?ktktjTj5ltltk#jOmsmsk5j_IVn@<sjDmINk?jDmROkAI4mmk]ttkukujXj:luluju7`k)9j+nI8787uIsR=kR2uj:m)KjuI47Qmg:74@S7S7=tj/IHlg8BjA7Mm@:)sR`q>kYq=q1qJI>]t@hIug=uj=m7Lk~oRXq5juuj?m@MjuRutl@Djs7sI_ItR>@stn7h@8Fj87JIJI9R4@ssq878DkJ@<tkJ@>]tIs)utkXjVnIWk=o)SIhI8GjKn@Yk>k/@8J78J7<7sgcqA@<)5]1)1)8Fq1o)tR:ujejVjVk>k#jtg8?k>otngsukGqBmto)8Djug8Hjug>]`n)s)8CjbnRYkQq3ngssk/7tIs)uskVoRVq4k6j/7SI_79)stogu@<tq3nsq8I:)47bo@8JI5tnRZkNj`j9j9jusk4k*k*kGoungt)=)tsmuo@8CjTnRXk8Is)_It@:R4ItItI8Cq8@eqBkFo)SIs)8J73RTn];)2sm@MjuR5@Tn);sq<q0qJ@>I2@Km)9)Gj71o@6gYn]=g6IYn]=sjcn]ZkS76RYng=tjPmtmtk8747Qmg=R0)ElI84n78787tujcnRXkMjBmINk9j>m7Lk87]770jKmgQkMjCmINjutq87>k]3R9ool]3)NmI;72@Km)8J@47S7S7=gA@uqISINjR4q5j;m)KjuujS@S@S@;)0IFlR88I)))7sI=kR2tj<m)Kju7XjtjtqFj*lRFjt73gPm]9]6RXnR=R2umINk872sm@Mk:j)II8@2RLm78DjBmINk?j?m@Mk9jWn)TkA73@NmI9)4gS@S@>Rt)?kg3ujA@Mm@:R4@S7S7=slR1j7_I47S7S7=]4IS7S7=uqJIElI82j5ltltk!jJ7Pm]<ujiikksqIumsmR4jRssj','7sskjljjq3mjnlojjktq74jkunkq@7ujnkj)71jjkI2jnjg3jjR77?nkj]I6jnljsjjk)8@7]78I75jR9jnlk777=nkj]];jkI7]778I=jnjk]75jskg7A77]@A@7AI77:nkj]gBjnjjjtl77]7Djj]:lI7]IFjjjunkj]gBjnjjjtnkj]=l]7Hjnklsjltjjjg]@5ol)7]7787JI7]7Kjj]?m77MjnkmI7OjjjR]@5ol)7]776ktjnjm]75l)Qjnlmsjjj@]@5ol)7]775mtjnjmujj]En)7Ujnkn@7WjjqI]@5ol)7]774nR7]7Qjj]J7Yjnlng77snkj]gBjnjnsjI_@7]7_I75m)`jo77]@bjl777R]@5ol)7]5oI75k7dko]7fjj]8@Kjosjnkl@77I]@5mRdkotjo]75k7hI7]7s)77@]@5mRdkq1jo]7s@75k7]7sI77@]@5m@sR7s]7dlq6jo]75k7]7sI77@]@5m)ssjq5loRIstjo]7sujj]9njq3jjknkj]J@Ljq9jq5moRRejq:jj]9njq3jjknkj]J7t@7s]gdmq<jo]75k)]7sI77@tR@t]Hq5q@dmq?jo]7tsjj]8I]7sI77@ttkq5mRdno]@5junjq3jjkqA@@s]OoR]tujo]7u)75jtnjq3jjkqA@@s]OoRgejqCjj]8@]7sI77@ttkq5mRdoo]7u@75jtnjq3jjkqA@@s]OoR]uI7ejqFjj]8@]7sI77@ttkq5mRdnqGjo]75junjq3jjkqA@@s]OoR]ejqHjj]8I]7sI77@]@5lgusjq5q7dmqAI7ejqJ@75junjq3jjknkj]J@uujq5noRRekj]9njq3jjknkj]JIKjj!jq5loRI077ejj&jj]9njq3jjknkj]Lj~jq5koRI0R7ejj]:njq3jjknkj]Nj+joRIejj,jj]:njq3jjknkj]OoR@0sjo]70tjj]:njq3jjknkj]g0ujjuj]1)75k7dkj1jo]75jum771@7]@1I77@]@5ol)7]6j4jj]9j5kj6jj8775jsj8@71ujnkj9jjmnkj]gBjnjjjR]@5lul)7]I2777g]@5ol)7]775nkj]Gj;jj<jnkj=jq777tnkj]gBjnjjj]2]7]72g75l@2sjnljA@771nkj]gBjnjjjg2ujnjjBjj]AI377]@3@73I773nkj]gBjnjjjsjFjnjjGjj]>jHjj?jnkjJ773tjjj]]@5ol)7]778@3ujnjjKjj];l)7]I47778@]@5ol)7]778I4@7]74I75jtjOjjPjnkjQjjS7779nkj]R4tjnkjSI779jTjnjjUjj]5jVjjWjnkjXjjkI]@5jjOjjYjnkjZjj_777;j_@7]87ajjtjjk]5ujnmj`jjJ@77>jajjbjR4@76I77A76R7]73jjejjS@') | |
--end of code | |
return img | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment