With a little help from a Base64 library I copied from lua-users wiki, I created this drawing app that is supposed to save an image to the camera roll. Triple-tapping brings up a window with instructions on how to save the image. Unfortunately, I learned that UIWebViews do not allow saving to the camera roll, so you cannot hold on the image to get the “save image” dialog to pop up. Any ideas on how to get it to work properly?
--# Base64
-- Base64.lua
-- #!/usr/bin/env lua
-- working lua base64 codec (c) 2006-2008 by Alex Kloss
-- compatible with lua 5.1
-- http://www.it-rfc.de
-- licensed under the terms of the LGPL2
-- modified by Rui Viana for gitty client
Base64 = class()
-- bitshift functions (<<, >> equivalent)
-- shift left
function Base64.lsh(value,shift)
return (value*(2^shift)) % 256
end
-- shift right
function Base64.rsh(value,shift)
return math.floor(value/2^shift) % 256
end
-- return single bit (for OR)
function Base64.bit(x,b)
return (x % 2^b - x % 2^(b-1) > 0)
end
-- logic OR for number values
function Base64.lor(x,y)
result = 0
for p=1,8 do result = result + (((Base64.bit(x,p) or Base64.bit(y,p)) == true) and 2^(p-1) or 0) end
return result
end
-- encryption table
local base64chars = {[0]='A',[1]='B',[2]='C',[3]='D',[4]='E',[5]='F',[6]='G',[7]='H',[8]='I',[9]='J',[10]='K',[11]='L',[12]='M',[13]='N',[14]='O',[15]='P',[16]='Q',[17]='R',[18]='S',[19]='T',[20]='U',[21]='V',[22]='W',[23]='X',[24]='Y',[25]='Z',[26]='a',[27]='b',[28]='c',[29]='d',[30]='e',[31]='f',[32]='g',[33]='h',[34]='i',[35]='j',[36]='k',[37]='l',[38]='m',[39]='n',[40]='o',[41]='p',[42]='q',[43]='r',[44]='s',[45]='t',[46]='u',[47]='v',[48]='w',[49]='x',[50]='y',[51]='z',[52]='0',[53]='1',[54]='2',[55]='3',[56]='4',[57]='5',[58]='6',[59]='7',[60]='8',[61]='9',[62]='-',[63]='_'}
-- function encode
-- encodes input string to base64.
function Base64.encode(data)
local bytes = {}
local result = ""
for spos=0,string.len(data)-1,3 do
for byte=1,3 do bytes[byte] = string.byte(string.sub(data,(spos+byte))) or 0 end
result = string.format('%s%s%s%s%s',
result,
base64chars[Base64.rsh(bytes[1],2)],
base64chars[Base64.lor(
Base64.lsh((bytes[1] % 4),4),
Base64.rsh(bytes[2],4))] or "=",
((#data-spos) > 1) and
base64chars[Base64.lor(Base64.lsh(bytes[2] % 16,2),
Base64.rsh(bytes[3],6))] or "=",
((#data-spos) > 2) and base64chars[(bytes[3] % 64)] or "=")
end
return result
end
-- decryption table
local base64bytes = {['A']=0,['B']=1,['C']=2,['D']=3,['E']=4,['F']=5,['G']=6,['H']=7,['I']=8,['J']=9,['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,['Q']=16,['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,['Y']=24,['Z']=25,['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,['g']=32,['h']=33,['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,['o']=40,['p']=41,['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,['w']=48,['x']=49,['y']=50,['z']=51,['0']=52,['1']=53,['2']=54,['3']=55,['4']=56,['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,['-']=62,['_']=63,['=']=nil}
-- function decode
-- decode base64 input to string
function Base64.decode(data)
local chars = {}
local result=""
for dpos=0,string.len(data)-1,4 do
for char=1,4 do chars[char] = base64bytes[(string.sub(data,(dpos+char),(dpos+char)) or "=")] end
result = string.format('%s%s%s%s',
result,
string.char(Base64.lor(Base64.lsh(chars[1],2), Base64.rsh(chars[2],4))),
(chars[3] ~= nil) and
string.char(Base64.lor(Base64.lsh(chars[2],4), Base64.rsh(chars[3],2))) or "",
(chars[4] ~= nil) and
string.char(Base64.lor(Base64.lsh(chars[3],6) % 192, (chars[4]))) or "")
end
return result
end
--# Main
-- Save to Camera Roll
-- Setup
function setup()
print("Hello Camera Roll!\
")
print("Tap and drag to paint\
Change brush settings with parameters\
Triple-tap to export to Camera Roll")
parameter.color("BrushColor")
parameter.number("BrushSize",1,20)
canvas = image(WIDTH,HEIGHT)
end
function draw()
-- Drawing
background(255)
strokeWidth(5)
-- Draw canvas
if canvas then
sprite(canvas,WIDTH/2,HEIGHT/2)
-- Draw border
noFill()
stroke(5)
rect(0,0,WIDTH,HEIGHT)
end
end
function touched(touch)
-- Draw to canvas if touch moving
if touch.state == MOVING then
setContext(canvas)
fill(BrushColor)
noStroke()
ellipse(touch.x,touch.y,BrushSize)
lineCapMode(ROUND)
smooth()
strokeWidth(BrushSize)
stroke(BrushColor)
line(touch.prevX,touch.prevY,touch.x,touch.y)
setContext()
end
-- Save to camera roll on triple tap
if touch.state == ENDED and touch.tapCount == 3 then
saveToCameraRoll(canvas)
end
end
--# MimeTypes
MIME = {
[".aac"] = "audio/aac",
[".abw"] = "application/x-abiword",
[".arc"] = "application/octet-stream",
[".avi"] = "video/x-msvideo",
[".azw"] = "application/vnd.amazon.ebook",
[".bin"] = "application/octet-stream",
[".bz"] = "application/x-bzip",
[".bz2"] = "application/x-bzip2",
[".csh"] = "application/x-csh",
[".css"] = "text/css",
[".csv"] = "text/csv",
[".doc"] = "application/msword",
[".eot"] = "application/vnd.ms-fontobject",
[".epub"] = "application/epub+zip",
[".gif"] = "image/gif",
[".htm"] = "text/html",
[".html"] = "text/html",
[".ico"] = "image/x-icon",
[".ics"] = "text/calendar",
[".jar"] = "application/java-archive",
[".jpeg"] = ".jpg",
[".js"] = "application/javascript",
[".json"] = "application/json",
[".mid"] = ".midi",
[".mpeg"] = "video/mpeg",
[".mpkg"] = "application/vnd.apple.installer+xml",
[".odp"] = "application/vnd.oasis.opendocument.presentation",
[".ods"] = "application/vnd.oasis.opendocument.spreadsheet",
[".odt"] = "application/vnd.oasis.opendocument.text",
[".oga"] = "audio/ogg",
[".ogv"] = "video/ogg",
[".ogx"] = "application/ogg",
[".otf"] = "font/otf",
[".png"] = "image/png",
[".pdf"] = "application/pdf",
[".ppt"] = "application/vnd.ms-powerpoint",
[".rar"] = "application/x-rar-compressed",
[".rtf"] = "application/rtf",
[".sh"] = "application/x-sh",
[".svg"] = "image/svg+xml",
[".swf"] = "application/x-shockwave-flash",
[".tar"] = "application/x-tar",
[".tif"] = "image/tiff",
[".tiff"] = "image/tiff",
[".ts"] = "video/vnd.dlna.mpeg-tts",
[".ttf"] = "font/ttf",
[".vsd"] = "application/vnd.visio",
[".wav"] = "audio/x-wav",
[".weba"] = "audio/webm",
[".webm"] = "video/webm",
[".webp"] = "image/webp",
[".woff"] = "font/woff",
[".woff2"] = "font/woff2",
[".xhtml"] = "application/xhtml+xml",
[".xls"] = "application/vnd.ms-excel",
[".xml"] = "XML",
[".xul"] = ".zip",
[".3gp"] = "video/3gpp",
[".3g2"] = "video/3gpp2",
[".7z"] = "application/x-7z-compressed"
}
--# SaveToCameraRoll
function saveToCameraRoll(img,v)
local textFont = font()
-- HTML template
local template = [[<!DOCTYPE html> <html> <head> <title>Save Image</title> <style>body{font-family:{{textFont}}; margin:20; color: #444;}img{width: 50%; position: relative; left: 25%; border: 1px dashed #444;}</style> </head> <body> <h1>Save Image</h1> <p>Hold on the image below until a save dialog appears</p><img src="{{src}}" alt="Image"> </body> </html>]]
-- Time/debug
local socket = require "socket"
local _print = print
local print = function(...)
if not v then
print(...)
end
end
local gt = socket.gettime
local finish = function(t)
print("Done ("..tostring(math.floor((gt()-t)*1000)).." ms)")
end
print "Saving image..."
local t = gt()
-- Save image
saveImage("Documents:temp",img)
print(readImage("Documents:temp"))
-- Setup path
local ENV = os.getenv("HOME")
local DOCUMENTS = "Documents"
local ASSETPACK = ""
local FILENAME = "temp.png"
local path = string.format("%s/%s/%s/%s",ENV,DOCUMENTS,ASSETPACK,FILENAME)
-- Open image
local data = io.open(path)
local content = data:read("*all")
data:close()
local img = image(content)
finish(t)
print(img)
print "Encoding content..."
t = gt()
local encodedContent = Base64.encode(content)
finish(t)
print "Generating HTML..."
t = gt()
local html = template
:gsub("{{textFont}}",textFont)
:gsub("{{src}}","data:image/png;base64,"..encodedContent)
finish(t)
print "Encoding HTML..."
t = gt()
local encodedHtml = Base64.encode(html)
finish(t)
print "Opening browser... \
Follow the instructions that appear.\
If the page fails to load, tap on the url bar and tap on 'Go' on your keyboard"
t = gt()
openURL("data:text/html;base64,"..encodedHtml,true) -- option 1: in-app browser
-- openURL("http://data:text/html;base64,"..encodedHtml,false) -- option 2: safari (fails to load)
finish(t)
collectgarbage()
end