Skip to content
IgorTimofeev edited this page Jul 17, 2021 · 13 revisions

This library implements its own image format for OpenComputers with a simple lossless compression algorithm. It allows you to load and save pictures in .pic format, as well as to perform simple operations like string serialization/de-serialization/scaling/transforming.

How does it work inside?

Every picture stored in RAM as a one-dimensional table, where the first two elements are picture width and height, and the next are pixel data. This approach is necessary for RAM saving. Here's a table example for better structure understanding:

{
  160, -- Image width
  50, -- Image height
  
  -- Begin of pixel data
  0xFF0000, -- Red background color
  0x0000FF, -- Blue symbol color,
  0x00, -- Transparency,
  "Q", -- Symbol,
  
  ... -- Same for every pixel from left to right / top to bottom
}

Loading/saving

image.load(string path): table or boolean result, nil or string reason

Try to load an image from file system by given path. If loading was successful, the image table will be returned, it can be used for drawing on interface - for example, via GUI.image() method. If loading fails, false and reason will be returned. Reason describes why this happened - for example, the file system is read-only or there is not enough RAM to load image

This method applies 24bit to 8bit color compression and color grouping methods for better HDD usage

local result, reason = image.load("test.pic")

if result then
  -- Do your stuff like GUI.image(1, 1, result)
else
  -- Image is broken or not enough RAM
end

image.save(string path, table picture, [, int encodingMethod = 6)]): boolean success, nil or string reason

Tries to save a given picture to a given path, using a specified encoding method. If no encoding method is given, it will be set to 6 automatically. Returns true if image was saved successfully, false and an reason if not (maybe filesystem is read-only or there's not enough disk space to save it)

local success, reason = image.load("test.pic")

if success then
  -- Image was saved
else
  -- Image wasn't saved (fs is read-only, not enough RAM or HDD free space)
end

String serialization

image.toString(table picture): string result

Serializes picture to string. Serialization is nearly slow, you should avoid this method if you don't really know what you're doing. It can be used for saving picture on disk in text (not binary) format and creating small standalone scripts that contains pictures directly in code with no need to load them from external file

This method applies basic 24bit to 8bit color compression, but doesn't apply color grouping compression to result, and it will be stored as linear sequence

image.toString(myImage)
> A03200BEAA0051...

image.fromString(string string): table result

De-serializes string as picture table. This method is not so fast as image.load(), but still useful for standalone scripts with directly included pictures instead of files

Take a note: when using very long serialized strings in code, you should use ... string syntax instead of "...", because of lua machine can truncate them:

image.fromString([[A03200BEAA0051...]])
> {
  160,
  50,
  
  0xDEDEDE,
  0xBBBBBB,
  0xFF,
  0x00,
  "Q",
  ...
}

Manual pixels manipulation

image.getWidth(table picture): int width

Returns pixel width of given picture. You can also access this value via picture[1]

image.getHeight(table picture): int height

Returns pixel height of given picture. You can also access this value via picture[2]

image.get(table picture, int x, int y): int background, int foreground, byte transparency, string symbol

Returns single pixel data by given x/y from picture

image.set(table picture, int x, int y, int background, int foreground, byte transparency, string symbol)

Sets single pixel data by given x/y to picture

image.getIndex(int x, int y, int pictureWidth)

Returns internal picture table position by given x/y and it's width. It can be used in performance-dependent programs with pixel sequences editing instead of calling image.set(...) all the time, because it internally uses getIndex(...) at every call

local picture= image.load("test.pic")
local width = image.getWidth(picture) -- For example, loaded picture width = 160 pixels

local from = image.getIndex(2, 1, width) -- from = 7
local to = image.getIndex(10, 2, width) -- to = 679

-- Let's change picture background from 2x1 to 10x2 pixels to green and symbol to "@"
-- Loop step is 4 because every pixel data contains 4 elements (background, foreground, transparency and symbol)
for i = from, to, 4 do
  picture[i] = 0x00FF00
  picture[i + 3] = "@"
end

image.create(int width, int height, [int background = 0x000000, int foreground = 0x000000, byte transparency = 0x00, string symbol = " ", boolean random = false]): table picture

Creates new picture instance by given width / height and pixel data. You can pass nil value to every pixel data - it will be automatically changed to default value.

If optional random argument is true, then every pixel data (except of transparency channel) will be random. Why someone needs random feature? Who knows...

image.copy(table picture): table copy

Performs deep copy of all pixel data of given picture and returns it as new picture. Of course, RAM usage will be doubled in this case

Convolution filters

image.convolve(table picture, table kernel): table processedPicture

Applies convolution kernel to given picture and returns processed picture as result. Note: because of presudographical concept of OpenComputers, foreground color and symbol will be set to 0x000000 and " " respectively

Every kernel is a one-dismensional table with first element belongs to its two-dismensional row size. For example, 3x3 identity kernel table should look like that:

{
  -- Row size
  3,
  -- Data
  1, 0, 0
  0, 1, 0,
  0, 0, 1
}

If you want to develop your own kernel generator, make sure that first element exists. You can read about convolution process and kernels here

image.getGaussianBlurKernel(int radius, float [0.0; 1.0] strength): table convolutionKernel

Creates new instance of gaussian blur kernel that can be applied to image using image.convolve() method. There are 2 parameters: radius represents how blurry result will be, and strength represents how blurred result will differ from original image

Example

First, we need to load the example picture and draw it on the screen using screen library:

-- Import libraries
local image = require("Image")
local screen = require("Screen")

----------------------------------------------------------------------

-- Clear old screen data with black color
screen.clear(0x0)
-- Load picture from disk
local picture = image.load("Example.pic")
-- Draw loaded picture on screen
screen.drawImage(1, 1, picture)
-- Update screen buffers forcely to show changes
screen.update(true)

As you can see, a nice raspberry basket shows on screen without any effects applied to the picture. Now let's use a convolution function and apply the blur effect:

-- Import libraries
local image = require("Image")
local screen = require("Screen")

----------------------------------------------------------------------

-- Clear old screen data with black color
screen.clear(0x0)
-- Load original picture from disk
local picture = image.load("Example.pic")
-- Create new gaussian blur convolution kernel with radius 5
local kernel = image.getGaussianBlurKernel(5, 1)
-- Apply blur filter
picture = image.convolve(picture, kernel)
-- Draw filtered picture on screen
screen.drawImage(1, 1, picture)
-- Update screen buffers forcely to show changes
screen.update(true)

Of course, some details were lost due to the limitations of the OpenComputers palette with 256 colors, as well as due to the pseudo-graphic system, but this still good enough for demonstration

Clone this wiki locally