Spritely -- a simple image editor

edited December 2011 in Code Sharing Posts: 1,255

I'm still scratching my head over the best approach to print, but this version does generate simple set statements to draw an image. I'm hoping to get more functionality into it soon.

Draw your sprite in the grid on the right, then copy the text from the output box to get the code to paste into your program.




  • This is really cool!
    The space in bottom right could be used to display a palette, so that you can reuse colors.

    (BTW, the order of blue and green seems to be reversed in drawBig.)

  • This is awesome. 8-bit Art in codea. yeah!

  • SimeonSimeon Admin Mod
    edited December 2011 Posts: 5,448

    Fantastic work Mark. I love that you put sound effects in for painting, and that you seem to have built the UI icons in your own editor, nice touch :)

    Regarding getting the data back out of the editor, there's a new, undocumented function in 1.2 you can use to make this awesome.

    loadstring() will let you execute a chunk of Lua code. So you can do the following instead of printing the text to the output window.

    • Store the image creation code as a big string using the custom data storage. Do the following: saveProjectData( "SpritelyData", "lots of image code as a string" )

    • The "lots of image code as a string" data would look something like this:

    local spritely = image( 16, 16 )
    spritely:set( 1, 4, 244, 100, 16 )
    spritely:set( 2, 4, 244, 100, 16 )
    spritely:set( 3, 4, 244, 100, 16 )
    spritely:set( 4, 4, 244, 100, 16 )
    -- ... and so on
    return spritely
    • In order to create an image from this saved data, all the user has to do is the following:
    createImage = loadstring( readProjectData("SpritelyData") )
    myImage = createImage() -- executes the above code
    sprite( myImage, x, y ) -- render image
    • Bonus points if you can allow the user to customise the name that spritely saves the data under (instead of "SpritelyData"). The user can have a quick way to create images from Spritely's saved data.

    Note: sprite editing is actually a feature I've wanted to add to Codea for a long time, and still might add.

    One way to allow the user to store the data under their own key could be to do the following, print something like this to the output window:

    -- Copy this data into your own sprite with the following code
    saveProjectData( "YOUR SPRITE NAME", readProjectData( "SpritelyData" ) )
    -- Load your new sprite into an image
    -- (Note the function call braces at the end of the line)
    myImage = loadstring( readProjectData( "YOUR SPRITE NAME" ) ) ()
  • edited December 2011 Posts: 11

    I wrote a little Python script which uses PIL to convert an image file to a series of "anImage:set(...)" statements: http://szleski.posterous.com/images-in-codea
    That way we can bundle custom sprites with a Codea project.

    python image2codea.py image.png

    Output: image.lua

  • Posts: 447

    Plus one for this

    How fast is codea in drawing images?

    The code i've been working on (pacman) requires drawing optimizations because i was using rect for drawing pixels, but with the new image functionality i imagine that's not the best way anymore

  • Posts: 1,255

    New version up that incorporates all these good suggestions.

    -- color palette for some pre-defined colors
    -- preview of image at actual size
    -- print to output fixed
    -- save to project string

    Simeon -- that loadString() function is killer. Solves all manner of problems. And building everything to one big screen and dumping it to print solved the nested loop issue.


  • Posts: 447

    Wow so awesome. How about talking to bortels about using his keyboard implementation (dont think he release yet) so you can type the name of the image as you save it?

  • Posts: 447

    i'd also allow currenttouch.state to be Moving as well and remove the old touch check so that the user can draw his finger around to draw stuff

  • Posts: 384

    This is great!

  • BortelsBortels Mod
    Posts: 1,557

    The keyboard class, as it stands (working, poorly, and ugly) is up on github already - https://github.com/bortels/HersheyCodea/blob/master/Keyboard.lua

    If someone wants to clean it up, feel free; I'm happy to accept push patches, or just publish a better one - I'm not proud. The keyboard has been sidetracked while I worked on image() stuff (nearly done, I swear!) and now I'm seriously considering redoing the font stuff I did with images for speed (ie. I'd pre-render the characters and draw them with images, rather than line drawing them each time).

  • SimeonSimeon Admin Mod
    Posts: 5,448

    Regarding drawing speed, @ruilov, Codea is a bit slow with sprites at the moment. We're going to be heavily optimising sprite drawing in the coming releases. noSmooth() rects are probably fastest.

    Are you drawing the level in Pacman as lines at the moment? It would be interesting to see how all the rects() compare to a single image of the level map speed-wise. I've only tried it on iPad 2 and it seemed perfectly smooth.

  • Posts: 447

    pacman map is drawing as rects. Each pixel from the original game corresponds to 2.5 x 2.5 pixels in codea. Drawing the rects for each tile of the game was slow (DeltaTime ~ 0.1) so added an optimization that joins two rects from different tiles if they have a side in common before drawing, which got DeltaTime to 0.02

  • SimeonSimeon Admin Mod
    Posts: 5,448

    @ruilov I suspect noSmooth() line() calls would be fastest, as these use the GL_LINE primitive, which is very cheap. What would be more interesting is to see if an image() with the level pixels set is faster to render using sprite().

    The way you did it is really cool though.

  • Posts: 1,255

    -- On @ruilov's suggestion, changed draw to support support MOVING.

    -- Added 1x and 2x views of image in progress.

    -- Added a load / save dialog with slots for multiple saved images

    -- general cleanup

    I'm looking at Bortels's keyboard class, but thought the load/save dialog would help till I get it working.


  • SimeonSimeon Admin Mod
    edited December 2011 Posts: 5,448

    Nice improvements. This is really amazing, nice features. I think it looks better with noSmooth() used in certain places - and runs faster. I modified the following functions:

    noSmooth() on scaled sprites uses nearest-neighbour interpolation, so you get a nice pixellated effect on the 2x sprite preview, rather than a blurriness. The noSmooth() on the main grid makes interaction faster, and looks much cleaner, I think.

    drawBig() and drawSmall() to incorporate noSmooth() as follows


    function drawBig()
        -- ...original drawBig() code, except change 
        strokeWidth(1) -- thinner strokeWidth looks good with noSmooth()


    function drawSmall()
        -- ...original drawSmall code up to:
        sprite( anImage, x, 412 )
        sprite( anImage, x + 1, 312, SpriteSize * 2, SpriteSize * 2 )
        -- Draw the numbers with smooth() style
  • SimeonSimeon Admin Mod
    Posts: 5,448

    Also, the load / save dialog box is excellent. I would also use noSmooth() on the preview images for each slot.

    The icons confused me initially, I thought the blue down-arrow was "Save" but it seems to be load. Perhaps you could use an "L" and an "S" on these icons as well.

  • Posts: 1,255

    The noSmooth() really does crisp things up. Nice.

    Down arrow should be save and up arrow load. If it's not working that way, I've mucked up something.

  • SimeonSimeon Admin Mod
    Posts: 5,448

    With 1.2.5 you will have to flip your images (previews and saved results), as image coordinates now start in the bottom left.

  • Posts: 447

    @Simon it's funny. The print button is now a fax machine

  • SimeonSimeon Admin Mod
    Posts: 5,448

    @ruilove THAT'S why I was confused about the save and load buttons :) @Mark - they were the right way around, I was just running your editor on a build of 1.2.6 and forgot the images would be upside down.

  • Posts: 1,255

    Yes, I should have realized that when you talked about a blue down arrow that you were using the update image format.

    This one should include noSmooth & have images flipped. I kept my internal grid top-left based and just modified the spots that translate between grid array and image.


  • The new version is awesome! Thanks for sharing this!

    I have one issue with the palette: when a color from palette is chosen the sliders no longer show correct values of RGBA. I guess this is how the iparameter's work when the variable value is changed in the code. I'm not sure how one could fix it.
  • Posts: 1,255
    Yeah, I can't think of any way to address this. I tried recreating the Iparameter after each color selection, but the sliders are unaffected.

    I've made a small update that adds a "global" button. This button saves the current image to global data rather than Spritely project data. Also included is a sample SpritelyLoader class to grab the global data and move it to your project.

    The set of images in the save / load dialog are saved to project data, so you'll need to shuffle globals over one at a time, but it beats copy/pasting the data in.

  • edited December 2011 Posts: 11
    One way would be to implement custom sliders, maybe as here:
    but where to place them? Either on top, or use full screen mode and place sliders on the left (there is no way then to copy output - but maybe it is not needed anymore?).

    BTW, I can't get watch() to work with object's attributes - watch("ss.val") does not work as expected.
  • Posts: 2,161

    As far as I understand it, watch only works on global variables. I iften watch("debug") and then set debug to mirror whatever I'm currently interested in.

  • Posts: 2,161

    (There's another example of my "i"-"o" displacement on an iPad keyboard.)

  • edited December 2011 Posts: 447

    Hey Mark, I merged spritely into an UI framework that I put together. Hope it's useful.

    Edit: http://ruilov.posterous.com/spritely-in-sup-01

  • Posts: 7

    Could someone post a simple program that uses Spritely to draw ( a simple box) and then uses spiritely loader to get that data into the project. I did read the comments and looked at the code for invaders but couldn't put everything together to get in working in my test program. I'm just trying to learn the program

  • Posts: 1,255

    There are basically four steps.

    1) draw the icon in Spritely and save it to global data by touching the globe icon. (you should hear a little whistle to let you know it worked)

    2) copy the text of the SpritelyLoader class into your project.

    3) create an instance of the SpriteLoader class in the setup of your main file. For example:

    myLoader = SpritelyLoader("bob")

    Now run your project. This will take the icon from the global data and save it in your project data under the name "bob."

    Once you've done this, you should remove the reference to the loader from your code. In fact, if you only want to load a single icon, you can now do away with the whole SpritelyLoader class. I know you can't see anything yet, but the loader's job is done.

    4) Now to display your icon. To do this, you'll load it into a string, and execute that string to build the image.

    createImage = loadstring( readProjectData("bob"))

    anImage = createImage() 

    You still can't see anything? That's okay. You need to add one more line to the draw() function of you project's main file.

    sprite(anImage, 100, 100)

    Now! The image stored as bob and rendered in anImage should finally be on the screen.

    Sorry that it's more than a bit awkward. As Codea goes forward, I'd bet we'll find it easier to save images and to move them between projects. When we do, I'll update Spritely.

  • Posts: 7

    Thank you very much Mark, this is what I was looking for! Keep up the fantastic work!

  • I'm sorry but I still can't quite figure out how to get the sprite into my project. I'm not sure if I followed the directions wrong? Is there a simpler way??

  • Posts: 1,255

    You could try just reading the image from the global data. Paste this into the draw() function and see what you get

    createImage = loadstring( readGlobalData("SpritelyGlobal"))

    anImage = createImage()

    sprite(anImage,100,1o0, 32, 32)

  • Do I need to have the Spritely Loader code in my project as well?

  • Never mind, got it working. However, I have a question. The sprite appears tiny, is there a way to enlarge it?

  • SimeonSimeon Admin Mod
    Posts: 5,448

    Render with sprite( image, width, height )

    Change the width and height to something larger. If you just specify the width, the height will be computed to preserve the aspect ratio of the image.

    If you turn on noSmooth() the image won't blur when it is upscaled.

  • How do you turn on noSmooth(). Sorry, I am a total beginner. First time ever coding

  • Posts: 1,255

    Just add noSmooth() to the draw() function (or setup) before you draw the sprite.

  • Ok. Thx. Love your program it works great. I'm working on merging it with Lua Jump so you will first draw your character and then begin the game.

  • JohnJohn Admin Mod
    edited December 2011 Posts: 643

    Made a picture of bender with it: http://yfrog.com/z/hs33nhp


  • Posts: 1,255

    Clearly you must go on to build a game in which Bender steals cigars and beer -- after all, dont forget the one commandment: god needs booze!

    Nice to see the this thing being used. Good work.

  • JohnJohn Admin Mod
    Posts: 643

    That sounds like fun, just got to finish Crabitron first :)

  • Posts: 1,255

    A minor update. I was creating some icons for... let's just say a game in which a chubby guy in coveralls jumps over things, and it became clear that being able to mirror and image image would be nice (so you don't have to draw both left and right facing versions of asymmetrical sprites). So I added a tool that does just that.

    I also changed the way the flood tool works. It occurred to me that it was kind of worthless the way it was, so I changed it to flood only blank pixels. Not sure that's much better. I may change it to do color-swapping instead.

    Did some general clean up and minor cosmetic adjustments.


  • BortelsBortels Mod
    Posts: 1,557

    Heh - I had to ask myself just that question (I just did a flood fill for my Pic library) - I suggest you make it flood fill pixels of the same color where you tap, ie. pick it, then tap a square - and it flood fills from that square out over all pixels of the same color. So you could change Bender's eyes above from yellow to red in two taps.

  • BortelsBortels Mod
    Posts: 1,557

    PS. I love the sample icons you have in there. :-)

  • I still haven't fixed the fact that the save and load arrows appear to be facing wrong way. Until a new release comes out this is relatively easy for people to do: under main add this saveBtn = Button(700, HEIGHT - 200, 5)
        loadBtn = Button(700, HEIGHT - 250, 3) instead of what is there for the saveBtn and loadBtn. All this does is switch the icons so that the save one will be where load goes and load will be where save goes making them face the right way since the pushing of buttons is really just touching that part of screen regardless of wheter or not the correct button is there. Hope this helps!

  • *u still haven't fixed, my bad not I :)

  • Posts: 1,255

    Have you upgraded to Codea 1.2.5? If so, the save arrow should be pointing down and the load arrow should be pointing up. To my mind, that was the "right" way... though I suppose that's open to interpretation.

  • Just got a chance to grab this. Thanks so much for it.

  • Posts: 1,255

    Thanks. I'm trying to think of what else would be nice here. Maybe buttons to nudge the image one pixel when it turns out your design started just a smidgen off?

  • SimeonSimeon Admin Mod
    Posts: 5,448

    Not sure if these would be useful, but here are some ideas:

    • An invert tool (just do {255 - r, 255 - g, 255 - b, a} for each pixel)
    • A box blur - a simple blur that averages pixels
    • Hue shift slider - rotates your colours through different hues (affects all pixels in the sprite). This would be handy for making colour variations of a particular icon.
    • Saturation slider (affects all pixels in the sprite)
Sign In or Register to comment.