KorIM Image Formats

KorIM support creating, loading and saving different image formats.

Table of contents:

ImageFormat, ImageFormats

ImageFormat allows to read/decode and write/encode images in a specific format. This class supports decoding static images, animations, and layered animated formats like ASE supporting most of its features.

abstract class ImageFormat(vararg exts: String) {
	val extensions: List<String>

    // Basic methods to implement
	open fun readImage(s: SyncStream, props: ImageDecodingProps = ImageDecodingProps()): ImageData = TODO()

    open fun readImageContainer(s: SyncStream, props: ImageDecodingProps = ImageDecodingProps()): ImageDataContainer

	open fun writeImage(
		image: ImageData,
		s: SyncStream,
		props: ImageEncodingProps = ImageEncodingProps("unknown")
	): Unit = throw UnsupportedOperationException()

    // Extended useful methods

	open fun decodeHeader(s: SyncStream, props: ImageDecodingProps = ImageDecodingProps()): ImageInfo?

	fun read(s: SyncStream, filename: String = "unknown"): Bitmap

	suspend fun read(file: VfsFile): Bitmap
	fun read(s: ByteArray, filename: String = "unknown"): Bitmap

	fun read(s: SyncStream, props: ImageDecodingProps = ImageDecodingProps()): Bitmap

	fun read(s: ByteArray, props: ImageDecodingProps = ImageDecodingProps()): Bitmap

	fun check(s: SyncStream, props: ImageDecodingProps = ImageDecodingProps()): Boolean

	fun decode(s: SyncStream, props: ImageDecodingProps = ImageDecodingProps()): Bitmap
	fun decode(s: ByteArray, props: ImageDecodingProps = ImageDecodingProps()): Bitmap

	fun encode(frames: List<ImageFrame>, props: ImageEncodingProps = ImageEncodingProps("unknown")): ByteArray

	fun encode(image: ImageData, props: ImageEncodingProps = ImageEncodingProps("unknown")): ByteArray
	fun encode(bitmap: Bitmap, props: ImageEncodingProps = ImageEncodingProps("unknown")): ByteArray

	suspend fun read(file: VfsFile, props: ImageDecodingProps = ImageDecodingProps()): Bitmap

data class ImageDecodingProps(
    val filename: String = "unknown",
    val width: Int? = null,
    val height: Int? = null,
    override var extra: ExtraType = null
) : Extra

data class ImageEncodingProps(
    val filename: String = "",
    val quality: Double = 0.81,
    override var extra: ExtraType = null
) : Extra


All the targets support at least native PNG and JPEG decoding:

expect val nativeImageFormatProvider: NativeImageFormatProvider

// This will try to use the native image format provider
suspend fun VfsFile.readBitmapOptimized()

Show images

suspend fun Bitmap.showImageAndWait(kind: Int = 0)
suspend fun ImageData.showImagesAndWait(kind: Int = 0) suspend fun List<Bitmap>.showImagesAndWait(kind: Int = 0)
suspend fun SizedDrawable.showImageAndWait(kind: Int = 0)


object RegisteredImageFormats : ImageFormat() {
    var formats: ImageFormats
    fun register(vararg formats: ImageFormat)
    fun unregister(vararg formats: ImageFormat)
    inline fun <T> temporalRegister(vararg formats: ImageFormat, callback: () -> T): T


open class ImageInfo : Extra by Extra.Mixin() {
	var width: Int = 0
	var height: Int = 0
	var bitsPerPixel: Int = 8

	val size: Size get() = Size(width, height)

	override fun toString(): String = "ImageInfo(width=$width, height=$height, bpp=$bitsPerPixel, extra=$extra)"

ImageDataContainer, ImageData, ImageAnimation, ImageFrame, ImageFrameLayer, ImageLayer

open class ImageDataContainer(
    val imageDatas: List<ImageData>
) {
    val imageDatasByName = imageDatas.associateBy { it.name }
    val default = imageDatasByName[null] ?: imageDatas.first()

    operator fun get(name: String?): ImageData? = imageDatasByName[name]

open class ImageLayer constructor(
    var index: Int,
    val name: String?

open class ImageData(
    val frames: List<ImageFrame>,
    val loopCount: Int = 0,
    val width: Int,
    val height: Int,
    val layers: List<ImageLayer> = fastArrayListOf(),
    val animations: List<ImageAnimation> = fastArrayListOf(),
    val name: String? = null,
) : Extra by Extra.Mixin() {
    val defaultAnimation: ImageAnimation
    val animationsByName: Map<String, ImageAnimation>
    val area: Int
    val framesByName: Map<String, ImageName>
    val framesSortedByProority: List<ImageFrame>
    val mainBitmap: Bitmap

data class ImageDataWithAtlas(val image: ImageData, val atlas: AtlasPacker.Result<ImageFrameLayer>)

open class ImageFrame(
    val index: Int,
    val time: TimeSpan = 0.seconds,
    val layerData: List<ImageFrameLayer> = emptyList(),
) : Extra by Extra.Mixin() {
    val first: ImageFrameLayer?
    val slice: BmpSlice
    val targetX: Int
    val targetY: Int
    val main: Boolean
    val includeInAtlas: Boolean

    val duration: TimeSpan
    val width: Int
    val height: Int
	val area: Int
    val bitmap: Bitmap
    val name: String?

open class ImageFrameLayer constructor(
    val layer: ImageLayer,
    slice: BmpSlice,
    val targetX: Int = 0,
    val targetY: Int = 0,
    val main: Boolean = true,
    val includeInAtlas: Boolean = true,
    val linkedFrameLayer: ImageFrameLayer? = null,
) {
    var slice: BmpSlice
    val width: Int
    val height: Int
    val area: Int
    val bitmap: Bitmap
    val bitmap32: Bitmap32

open class ImageAnimation(
    val frames: List<ImageFrame>,
    val direction: Direction,
    val name: String,
    val layers: List<ImageLayer>
) {
    enum class Direction { FORWARD, REVERSE, PING_PONG }

Packing ImageDataContainer and ImageData into an atlas in runtime

fun ImageData.packInMutableAtlas(mutableAtlas: MutableAtlas<Unit>): ImageData
fun ImageDataContainer.packInMutableAtlas(mutableAtlas: MutableAtlas<Unit>): ImageDataContainer 


KorIM supports lots of formats out of the fox


ASEPrite image format. Supports reading.


Bitmap uncompressed format. Supports reading and writing.


Supports reading DDS files, and DXT encoded images.


Supports decoding static and animated GIF files.


Supports reading windows icon files.


Supports reading krita image files.


Supports reading and writing PNG (Portable Network Graphics) files.


Supports reading PSD files (only the flattened global layer for now)


Supports reading SVG and rasterizing it to the default size. (For SVG vector reading check the vector graphics section)


Supports reading and writing Targa uncompressed image files.

Reading images

You can read images from files in any location with Vfsfile.

You can use the format= argument in the reading functions to specific a specific format or a ImageFormats group, or register the formats you are going to use into the RegisteredImageFormats;

suspend fun VfsFile.readBitmap()
suspend fun VfsFile.readBitmapOptimized()
suspend fun VfsFile.readImageDataContainer()