Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Workaround for Saving to Camera Roll

edited October 2018 in Code Sharing Posts: 194

--# Main -- Save to Camera Roll -- Setup function setup() print("Hello Camera Roll!\n") print("Tap and drag to paint\nChange brush settings with parameters\nTriple-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 =, "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... \nFollow 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(, "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\nContent-Length: %d\nContent-Type: image/png\nConnection: close\n\n") :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\nContent-Length: %d\nContent-Type: text/html\nConnection: close\n\n") :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.



  • dave1707dave1707 Mod
    edited October 2018 Posts: 8,720

    @em2 This is an interesting way of saving the image. Sometimes I would get an error saying it couldn’t connect to the server, but most of the time it worked OK. I made a change for myself. Instead of using a triple tap to save the image, I used parameter.action("Save image",saveCanvas) and added the function saveCanvas() below. So instead of a triple tap, I just press the Save image parameter button. Even though this works, doing a screen snapshot is a little easier. Maybe a Codea function could be added in a future version to save the screen image to the camera roll. Anyways, this is a good example for everyone to look at, including myself.

    function saveCanvas()

    PS I increased the settimeout value to 3 and haven’t received the server error yet.

  • em2em2
    Posts: 194
    @dave1707 good tip for the timeout value, thanks.
    I agree screenshotting might be easier, but I find this function useful for saving PNG's with alpha or with higher resolution.
    The same principle may also be useful for serving up other kinds of files, now that I think of it... Hmm...
  • Posts: 505

    hm, that's actually quite interesting approach! thank you for sharing.

Sign In or Register to comment.