Color buffers
A color buffer is an image stored in GPU memory.
Creating a color buffer
Color buffers are created using the colorBuffer()
function.
val cb = colorBuffer(640, 480)
Specifying buffer format
Color buffers can be created in different formats. The buffer format specifies the number and order of channels in the image. Color buffers can have 1 to 4 channels. The format
argument can be any ColorFormat
value.
val cb = colorBuffer(640, 480, format = ColorFormat.R)
Specifying buffer type
The buffer type specifies which data type is used for storing colors in the buffer. The type
argument can be any ColorType
value.
val cb = colorBuffer(640, 480, type = ColorType.FLOAT16)
Loading color buffers
Color buffers can be loaded from an image stored on disk. Supported file types are png, jpg, dds and exr (OpenEXR).
val cb = loadImage("data/images/pm5544.jpg")
Generating color buffers
Use drawImage
to create a color buffer and draw into it.
fun main() = application {
program {
val gradientBackground = drawImage(width, height) {
// Draw anything here, for example a radial gradient.
drawer.shadeStyle = RadialGradient(ColorRGBa.WHITE, ColorRGBa.PINK)
val r = Rectangle.fromCenter(drawer.bounds.center, 800.0, 800.0)
drawer.rectangle(r)
}
extend {
drawer.image(gradientBackground)
drawer.circle(drawer.bounds.center, sin(seconds) * 80 + 100)
}
}
}
drawImage
is convenient for creating static images. If a program requires redrawing a color buffer use a render target instead.
Freeing color buffers
If a program creates new buffers while it runs it is important to free those buffers when no longer needed to avoid running out of memory.
// -- When done using the buffer call destroy() to free its memory.
cb.destroy()
Saving color buffers
Color buffers can be saved to disk using the saveToFile
member function. Supported file types are png, jpg, dds and exr (OpenEXR).
val cb = colorBuffer(640, 480)
cb.saveToFile(File("output.jpg"))
When repeatedly saving color buffers asynchronously (the default) it is possible to run out of memory. This can happen if the software can not save images at the requested frame rate. In such situations we can either set async = false
in saveToFile()
or avoid saveToFile
and use the VideoWriter together with pngSequence or tiffSequence instead.
Note that some image file types take longer to save than others.
Copying between color buffers
Color buffer contents can be copied using the copyTo
member function. Copying works between color buffers of different formats and types.
// -- create color buffers
val cb0 = colorBuffer(640, 480, type = ColorType.FLOAT16)
val cb1 = colorBuffer(640, 480, type = ColorType.FLOAT32)
//-- copy contents of cb0 to cb1
cb0.copyTo(cb1)
Writing into color buffers
To upload data into the color buffer one uses the write
member function.
// -- create a color buffer that uses 8 bits per channel (the default)
val cb = colorBuffer(640, 480, type = ColorType.UINT8)
// -- create a buffer (on CPU) that matches size and layout of the color buffer
val buffer = ByteBuffer.allocateDirect(cb.width * cb.height * cb.format.componentCount * cb.type.componentSize)
// -- fill buffer with random data
for (y in 0 until cb.height) {
for (x in 0 until cb.width) {
for (c in 0 until cb.format.componentCount) {
buffer.put((Math.random() * 255).toInt().toByte())
}
}
}
// -- rewind the buffer, this is essential as upload will be from the position we left the buffer at
buffer.rewind()
// -- write into color buffer
cb.write(buffer)
Reading from color buffers
To download data from a color buffer one uses the read
member function.
// -- create a color buffer that uses 8 bits per channel (the default)
val cb = colorBuffer(640, 480, type = ColorType.UINT8)
// -- create a buffer (on CPU) that matches size and layout of the color buffer
val buffer = ByteBuffer.allocateDirect(cb.width * cb.height * cb.format.componentCount * cb.type.componentSize)
// -- download data into buffer
cb.read(buffer)
// -- read the first UINT8 pixel's color
val r = buffer[0].toUByte().toDouble() / 255.0
val g = buffer[1].toUByte().toDouble() / 255.0
val b = buffer[2].toUByte().toDouble() / 255.0
val a = buffer[3].toUByte().toDouble() / 255.0
val c = rgb(r, g, b, a)
Color buffer shadows
To simplify the process of reading and writing from and to color buffers we added a shadow buffer to ColorBuffer
. A shadow buffer offers a simple interface to access the color buffer’s contents.
Note that shadow buffers have more overhead than using read()
and write()
.
// -- create a color buffer that uses 8 bits per channel (the default)
val cb = colorBuffer(640, 480, type = ColorType.UINT8)
val shadow = cb.shadow
// -- download cb's contents into shadow
shadow.download()
// -- place random data in the shadow buffer
for (y in 0 until cb.height) {
for (x in 0 until cb.width) {
shadow[x, y] = ColorRGBa(Math.random(), Math.random(), Math.random())
}
}
// -- upload shadow to cb
shadow.upload()