--# 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
--# SaveToCameraRoll
function saveToCameraRoll(img)
-- Font for styling output
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>'
local socket = require "socket"
-- Save image temporarily
saveImage("Documents:temp",img)
-- 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 to read raw data
local data = io.open(path, "rb")
local content = data:read("*all")
data:close()
local img = image(content)
-- Generate html
local html = template
:gsub("{{textFont}}",textFont)
:gsub("{{src}}","img.png")
print "Opening browser... \
Follow the instructions that appear."
-- Create a webserver that serves two requests: one for the html page and one for the image
server = assert(socket.tcp()) -- create a simple tcp master object
assert(server:bind("localhost", 0)) -- bind it to localhost, at a random port
server:listen(5) -- listen for connections (backlog 5)
server:settimeout(1) -- max timeout 1 second, so we don't get stuck in an infinite waiting loop
local ip, port = server:getsockname() -- get the ip and port
openURL("http://"..ip..":"..port, false) -- open Safari
-- This should loop twice
while true do
local client,err = server:accept() -- wait for connections
if client then
local line, err = client:receive() -- get data from client
local route = line:match("GET (/[^ ]*)")
if not err then
-- Any other path besides root will be served the image, then the loop will end and the server will close
if route ~= "/" then
local f = assert(io.open(path, "rb"))
local current = f:seek()
local length = f:seek("end") -- get the length of the image file
f:seek("set", current) -- go back to original position
-- Send initial headers
client:send(
("HTTP/1.0 200 OK\
Content-Length: %d\
Content-Type: image/png\
Connection: close\
\
")
:format(length)
)
-- Read the file in chunks
local size = 2^13 -- good buffer size (8K)
while true do
local block = f:read(size)
if not block then break end
client:send(block)
end
f:close()
break
end
client:send(
("HTTP/1.0 200 OK\
Content-Length: %d\
Content-Type: text/html\
Connection: close\
\
")
:format(#html)
)
client:send(html)
end
client:close()
else
break
end
end
-- Final cleanup
os.remove(path) -- remove file
server:close() -- kill the server
collectgarbage()
end
saveToCameraRoll
is a function that gets a codeaimage and provides a way for the user to save it as a photo in the Photos app. The process is:
- The image is saved temporarily, and its raw data is read by
io
- A server is created that serves the image file and some html
- The user is directed to the server’s webpage, from where the image can be held until the “save to camera roll” dialog appears
I included a sample drawing app for testing.