←  APIs and Utilities

ComputerCraft | Programmable Computers for Minecraft

»

Surface API 1.6.2

CrazedProgrammer's Photo CrazedProgrammer 15 Mar 2015

The Surface API is a really powerful drawing API for ComputerCraft.
It uses the least amount of drawing calls possible to reduce lag and tearing.
It is compatible with all displays, but if the display only supports black and white
then you can only use colors.black and colors.white.
A surface is a texture that you can draw to.
It has 3 layers: the character, background color and text color layer.
The data of those 3 layers are stored in one sequential buffer so it uses the least amount of RAM.
Surfaces are wrapped in tables with the width, height, buffer and functions in it.
There are many useful functions, such as: clearing the surface, drawing text, pixels, lines, rectangles, scaling surfaces and more!
You can also load and save surfaces with the NFP format for the built-in paint,
or the more advanced NFT format which also supports text for programs such as NPaintPro and Sketch.
This is a complete replacement for the build-in paintutils API and maybe even the term API.
There is no error checking to keep the performance high, so if you get errors check if you have correct inputs.

You can download the source here: (34.35kb)
pastebin get 5YWfPd8Z surface
All functions are documented here:
https://github.com/C...SurfaceAPI/wiki (click on the function name to get to the documentation)

And then you can simply load it using os.loadAPI:
os.loadAPI("surface")
This program will display Hello, world! for two seconds with an orange background and blue text:
-- Loads the surface API
os.loadAPI("surface")
-- Creates a new surface to draw on:
-- surface.create(width, height, char, backcolor, textcolor)
local surf = surface.create(51, 19, " ", colors.orange, colors.blue)
-- Draws text at the desired location:
-- surf:drawText(x, y, text, backcolor, textcolor)
surf:drawText(3, 3, "Hello, world!")
-- Draws the surface to the screen:
-- surface.render(surface, display, x, y, sx1, sy1, sx2, sy2)
surf:render(term)
-- Waits for two seconds
os.sleep(2)
Screenshot:
Posted Image

Surfaces also support transparency.
Because surfaces have 3 layers, you can choose which layers you change by nil-ing out the ones you don’t.
For example if you only want to change the background color:
surf:drawPixel(3, 3, nil, colors.black)
If you want to overwrite all layers then you can set surf.overwrite to true.
surf.overwrite = true

Here is an example of what you can do with the scaling and rotating algorithm:
-- Loads the surface API
os.loadAPI("surface")
-- Creates an orange surface
local surf = surface.create(51, 19, " ", colors.orange, colors.white)
-- Loads a picture of mario from the file mario.nft
local surf2 = surface.load("mario.nft")
-- Draws a small mario
surf:drawSurfaceScaled(2, 2, 7, 9, surf2)
-- Draws a normal mario
surf:drawSurface(11, 2, surf2)
-- Draws a rotated mario
surf:drawSurfaceRotated(33, 10, 7, 9, 0.4, surf2)
-- Draws the surface to the computer display
surf:render(term)
-- Waits for two seconds
os.sleep(2)
Screenshot:
Posted Image

If you want to save disk space, you can delete functions that you don’t use in the API code.
However, some functions require other functions to function.
You can view the dependencies in the function documentation.

Changelog:
Version 1.6.2:
- Fixed term.scroll, term.clearLine and term.setCursorBlink from surf:getTerm
Version 1.6.0:
- Added surf:drawHLine and surf:drawVLine
- Added the ability to remove functions from the API code to save disk space (look at the function documentation for dependencies)
- Removed all global references (surface.*) so you can load the API as something else than "surface" or load it locally
Version 1.5.3:
- Fixed surf:render function
Version 1.5.2:
- Big performance improvements (about 80% on surf:render)
Version 1.5.1:
- Fixed surf:drawArc, surf:drawPie and surf:fillPie using inverted angles
Version 1.5.0:
- Added surf:getBounds
- Added surf:copy
- Added surf:getTerm
- Added surf:drawRoundedRect and surf:fillRoundedRect
- Added surf:drawArc
- Added surf:drawPie and surf:fillPie
- Added surf:shader
- Added surf:shift
- Fixed a couple bugs
Version 1.4.0:
- Completely rewritten and optimized
- New version numbering and variable: surface.version
- Added surf:drawRoundRect and surf:fillRoundRect
Version 1.3:
- Added term.blit support
- surf:render no longer throws an error with transparent surfaces
- Added a new image format: SRF (only uses hex characters and underscores)
- Added surface.loadString and surf:saveString
- Added surf:drawLines
- Added surf:drawTriangles and surf:fillTriangles
Version 1.2:
- Added surf:drawSurfaceRotated
Version 1.1:
- Replaced surf:drawToDisplay with surf:render
- Added surf:setBounds
- Added surf:drawTriangle and surf:fillTriangle
- Added surf:drawEllipse and surf:fillEllipse
- Added surf:floodFill

Older version downloads:
1.5.3: pastebin get J2Y288mW surface
1.4.0: pastebin get vm1y156i surface
1.3: pastebin get ajFj7yNK surface
1.2: pastebin get hwAbAgLD surface
1.1: pastebin get AmmVZusd surface
1.0: pastebin get g87xZ4dA surface

If you have any suggestions, bug reports or questions then please leave a reply or send a PM.
Edited by CrazedProgrammer, 07 June 2016 - 08:14 PM.
Quote

InDieTasten's Photo InDieTasten 15 Mar 2015

Looks nice, although I don't have any applications of this
Quote

CrazedProgrammer's Photo CrazedProgrammer 15 Mar 2015

View PostInDieTasten, on 15 March 2015 - 09:25 PM, said:

Looks nice, although I don't have any applications of this
Thanks!
This API is mainly intended for advanced GUIs and games, so if you ever make such a program
it's way easier to use this API than the term API :D
Edited by CrazedProgrammer, 15 March 2015 - 09:31 PM.
Quote

Exerro's Photo Exerro 15 Mar 2015

Great API, great documentation. This is awesome.

Things you could add:
- Stencils (limit the area you can draw to)
- Flood fill
- :drawEllipse() and :drawPolygon()
- Improve rendering to
-- a) Only draw changes
-- b ) Draw more than one pixel at a time if it has the same background colour and text colour.
Edited by awsumben13, 15 March 2015 - 09:36 PM.
Quote

CrazedProgrammer's Photo CrazedProgrammer 15 Mar 2015

View Postawsumben13, on 15 March 2015 - 09:35 PM, said:

Great API, great documentation. This is awesome.

Things you could add:
- Stencils (limit the area you can draw to)
- Flood fill
- :drawEllipse() and :drawPolygon()
- Improve rendering to
-- a) Only draw changes
-- b ) Draw more than one pixel at a time if it has the same background colour and text colour.
Thanks for the great feedback!
I will add floodfill, ellipses and polygons soon! (maybe tomorrow if the algorithms aren't that hard)
I don't know how stencils would work, so please explain :D
The rendering IS actually doing what you're suggesting.
It cycles through the pixels like scanlines and if a color changes (background or text), it changes the color and draws all the characters of the pixels that have come before it.
It puts the commands inside a table and then cycles through them all at once so there is minimal tearing.
Edited by CrazedProgrammer, 15 March 2015 - 09:54 PM.
Quote

TheOddByte's Photo TheOddByte 15 Mar 2015

View PostCrazedProgrammer, on 15 March 2015 - 09:19 PM, said:

-- Creates an orange surface
local surf = surface.create(51, 19, " ", colors.black, colors.white)
Wouldn't that be a black surface? :P
Anyway, haven't tested this but it looks quite good, I'm most interested in the scaling function. It also feels like some of your functions could have simpler names
drawToDisplay    > render
drawSurfacePart > drawQuad or quad

Oh, and I don't know how moderators feel, but I believe it's discouraged to post your email openly like that.
Quote

CrazedProgrammer's Photo CrazedProgrammer 15 Mar 2015

View PostTheOddByte, on 15 March 2015 - 09:57 PM, said:

View PostCrazedProgrammer, on 15 March 2015 - 09:19 PM, said:

-- Creates an orange surface
local surf = surface.create(51, 19, " ", colors.black, colors.white)
Wouldn't that be a black surface? :P
Anyway, haven't tested this but it looks quite good, I'm most interested in the scaling function. It also feels like some of your functions could have simpler names
drawToDisplay	> render
drawSurfacePart > drawQuad or quad

Oh, and I don't know how moderators feel, but I believe it's discouraged to post your email openly like that.
Whoops! I made the documentation with mostly black surfaces, and I forgot about that one! :P
The surf:render() name is a great suggestion, and I'll add it in the next version (which will be out probably tommorow with floodfill, ellipses and polygons).
The surf:drawQuad() or surf:quad function doesn't really explain that it draws surfaces, I'd like to hear other people's opinions about that :D
The scaling algorithm is basically a nearest neighbor algorithm which I developed in maths class because I was bored.
You can look it up in the source if you want to know how it works.
The email is not my personal email, so I don't mind if people send me emails :P
Edited by CrazedProgrammer, 15 March 2015 - 10:09 PM.
Quote

Exerro's Photo Exerro 15 Mar 2015

View PostCrazedProgrammer, on 15 March 2015 - 09:44 PM, said:

View Postawsumben13, on 15 March 2015 - 09:35 PM, said:

-snip-
Thanks for the great feedback!
I will add floodfill, ellipses and polygons soon! (maybe tomorrow if the algorithms aren't that hard)
I don't know how stencils would work, so please explain :D
The rendering IS actually doing what you're suggesting.
It cycles through the pixels like scanlines and if a color changes (background or text), it changes the color and draws all the characters of the pixels that have come before it.
It puts the commands inside a table and then cycles through them all at once so there is minimal tearing.

In my library, it uses 'buffers' rather than 'surfaces'. There are a couple of functions...
buff:setStencil( x, y, width, height )
s = buff:addStencil( x, y, width, height )
buff:removeStencil( s )

Adding a stencil just does a bbox with the current stencil so it's the area that fits inside both stencils (the current one and added one). Remove removes the last stencil added and updates. Set just sets the stencil.

Now, if you had this:
buff:setStencil( 5, 5, 3, 3 )
then tried...
gfx2D.rectangle( 4, 4, 3, 3 ) -- after wrapping to the buffer, setting colours and characters, etc
It would only draw from 5, 5 to 6, 6 rather than 4, 4, to 6, 6 because it won't draw outside the stencil's area.

This is essential for me when writing a UI library, because scrolling frames' objects will most often try and draw where they shouldn't be allowed to, and by using stencils, it is possible to stop them drawing there.

Personally, I wouldn't use :drawQuad() and just stick to :drawSurfacePart(). I wouldn't even replace :drawSurface() with :render(), maybe just add render as an optional one like...
object.render = object.drawSurface
Quote

CrazedProgrammer's Photo CrazedProgrammer 16 Mar 2015

View Postawsumben13, on 15 March 2015 - 10:13 PM, said:

View PostCrazedProgrammer, on 15 March 2015 - 09:44 PM, said:

View Postawsumben13, on 15 March 2015 - 09:35 PM, said:

-snip-
Thanks for the great feedback!
I will add floodfill, ellipses and polygons soon! (maybe tomorrow if the algorithms aren't that hard)
I don't know how stencils would work, so please explain :D
The rendering IS actually doing what you're suggesting.
It cycles through the pixels like scanlines and if a color changes (background or text), it changes the color and draws all the characters of the pixels that have come before it.
It puts the commands inside a table and then cycles through them all at once so there is minimal tearing.

In my library, it uses 'buffers' rather than 'surfaces'. There are a couple of functions...
buff:setStencil( x, y, width, height )
s = buff:addStencil( x, y, width, height )
buff:removeStencil( s )

Adding a stencil just does a bbox with the current stencil so it's the area that fits inside both stencils (the current one and added one). Remove removes the last stencil added and updates. Set just sets the stencil.

Now, if you had this:
buff:setStencil( 5, 5, 3, 3 )
then tried...
gfx2D.rectangle( 4, 4, 3, 3 ) -- after wrapping to the buffer, setting colours and characters, etc
It would only draw from 5, 5 to 6, 6 rather than 4, 4, to 6, 6 because it won't draw outside the stencil's area.

This is essential for me when writing a UI library, because scrolling frames' objects will most often try and draw where they shouldn't be allowed to, and by using stencils, it is possible to stop them drawing there.

Personally, I wouldn't use :drawQuad() and just stick to :drawSurfacePart(). I wouldn't even replace :drawSurface() with :render(), maybe just add render as an optional one like...
object.render = object.drawSurface
Thanks for explaining how stencils work!
I think I can implement stencils with the name surf:setBounds(x1, y1, x2, y2), but it won't have a stencil stack.
I will replace surf:drawToDisplay with surf:render because it's short and makes sense.
Thanks for your opinion on surf:drawQuad :D
I will keep surf:drawSurfaceScaled like it is.
Quote

CrazedProgrammer's Photo CrazedProgrammer 16 Mar 2015

I have updated the Surface API to version 1.1!
I have made these changes and additions to the API:
- Replaced surf:drawToDisplay with surf:render
- Added surf:setBounds
- Added surf:drawTriangle and surf:fillTriangle
- Added surf:drawEllipse and surf:fillEllipse
- Added surf:floodFill

You can download the new version from the same pastebin as before:
pastebin get 5YWfPd8Z surface
If you want to download version 1.0 for some reason you can still do that:
pastebin get g87xZ4dA surface
Quote

SpencerBeige's Photo SpencerBeige 16 Mar 2015

resizing images? wow. anew form of amazing has come ;p
Quote

CrazedProgrammer's Photo CrazedProgrammer 16 Mar 2015

View Postslow-coder, on 16 March 2015 - 09:20 PM, said:

resizing images? wow. anew form of amazing has come ;p
Thanks!
I will probably add rotating surfaces soon so you can combine the two to make more forms of amazing :P
Quote

Geforce Fan's Photo Geforce Fan 16 Mar 2015

View PostCrazedProgrammer, on 15 March 2015 - 09:31 PM, said:

View PostInDieTasten, on 15 March 2015 - 09:25 PM, said:

Looks nice, although I don't have any applications of this
Thanks!
This API is mainly intended for advanced GUIs and games, so if you ever make such a program
it's way easier to use this API than the term API :D
I think it'd be mostly useful for an OS with a lot of animations like OneOS. Or a side-scroller that has zooming.

You might want to make a makeActive() function. So, when a buffers is active, when you do something to it, it not changes variables in the buffer, but also draws it to the screen.
Quote

CrazedProgrammer's Photo CrazedProgrammer 16 Mar 2015

View PostGeforce Fan, on 16 March 2015 - 09:31 PM, said:

View PostCrazedProgrammer, on 15 March 2015 - 09:31 PM, said:

View PostInDieTasten, on 15 March 2015 - 09:25 PM, said:

Looks nice, although I don't have any applications of this
Thanks!
This API is mainly intended for advanced GUIs and games, so if you ever make such a program
it's way easier to use this API than the term API :D
I think it'd be mostly useful for an OS with a lot of animations like OneOS. Or a side-scroller that has zooming.

You might want to make a makeActive() function. So, when a buffers is active, when you do something to it, it not changes variables in the buffer, but also draws it to the screen.
This API is mainly intended for advanced GUIs and all sorts of games (not just sidescrollers), so you're right :D
I won't add a function that draws automatically because it would redraw the whole surface every time a function is called.
That is not only very inefficient, but it also isn't really practical.
It is more convenient to call surf:render when you want to draw a surface to a display.
Thanks for the feedback!
Edited by CrazedProgrammer, 16 March 2015 - 09:40 PM.
Quote

Exerro's Photo Exerro 16 Mar 2015

This is by far the most useful API I've seen on the forums. I'm taking serious design tips from it, and I'm annoyed I didn't have these things implemented from the start of writing Nova. Rotation would be incredible to have for animations.

One quick question... How did you implement :drawTriangle()? Surely you're using the scanline method, and if so, it's not any harder to implement a :drawPolygon() function.
Quote

CrazedProgrammer's Photo CrazedProgrammer 16 Mar 2015

View Postawsumben13, on 16 March 2015 - 10:14 PM, said:

This is by far the most useful API I've seen on the forums. I'm taking serious design tips from it, and I'm annoyed I didn't have these things implemented from the start of writing Nova. Rotation would be incredible to have for animations.

One quick question... How did you implement :drawTriangle()? Surely you're using the scanline method, and if so, it's not any harder to implement a :drawPolygon() function.
Thank you so much!
It means a lot to me when I get feedback from people who really like my creations.
Also, your OS is one of the best, and you may modify and include the Surface API wherever you want.
As for the draw/fillTriangle functions, I've tried all three triangle drawing algorithms (scanline, barycentric and bresenham)
and the scanline and barycentric algorithms had too many artifacts.
I decided to make triangles out of normal bresenham lines.
For the fillTriangle function I used a sequential buffer containing booleans to draw the lines
and then I used scanlines to fill the space in between the two lines.
I have looked up numerous articles on how you can draw filled polygons but they are either too complicated or they have too many artifacts.
If you find a solid solution to this, I'd be really happy to include it.
I am going to work hard on implementing rotating surfaces (I've already done something like that before so it shouldn't be too hard) :D
Quote

CrazedProgrammer's Photo CrazedProgrammer 18 Mar 2015

I have updated the Surface API to version 1.2!
I have made one addition and that is:
- Added surf:drawRotatedSurface
You can now draw rotated surfaces!

You can download the new version from the same pastebin as before:
pastebin get 5YWfPd8Z surface
If you want to download version 1.1 for some reason you can still do that:
pastebin get AmmVZusd surface
Quote

CrazedProgrammer's Photo CrazedProgrammer 06 Apr 2015

I updated the Surface API to version 1.3!
I made these changes and additions to the API:
- Added term.blit support
- surf:render no longer throws an error with transparent surfaces
- Added a new image format: SRF (only uses hex characters and underscores)
- Added surface.loadString and surf:saveString
- Added surf:drawLines
- Added surf:drawTriangles and surf:fillTriangles

You can download the new version from the same pastebin as before:
pastebin get 5YWfPd8Z surface
If you want to download version 1.2 for some reason you can still do that:
pastebin get hwAbAgLD surface
Edited by CrazedProgrammer, 06 April 2015 - 06:31 PM.
Quote

CrazedProgrammer's Photo CrazedProgrammer 16 Apr 2015

Since Dan fixed the term.blit function that was broken in ComputerCraft beta 1.74pr18 I've made a small fix to the Surface API.
You can download it from the same pastebin as before:
pastebin get 5YWfPd8Z surface
If you want to fix it without downloading the new version you just have to remove the "15 - " on lines 316 and 317.
Quote

Creator's Photo Creator 16 Apr 2015

View Postawsumben13, on 16 March 2015 - 10:14 PM, said:

This is by far the most useful API I've seen on the forums. I'm taking serious design tips from it, and I'm annoyed I didn't have these things implemented from the start of writing Nova. Rotation would be incredible to have for animations.

One quick question... How did you implement :drawTriangle()? Surely you're using the scanline method, and if so, it's not any harder to implement a :drawPolygon() function.

@awesumben every post that is about gui you ask for a triangle drawing function and a polygon drawing function. What is the math behind circles triangles and polygons in general?
Quote