You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
9.4 KiB
340 lines
9.4 KiB
4 years ago
|
# Image and in-app icons manipulations
|
||
|
|
||
|
## What is covered
|
||
|
|
||
|
In this tutorial we will show you how to work with images using Compose for Desktop.
|
||
|
|
||
|
## Loading images from resources
|
||
|
|
||
|
Using images from application resources is very simple. Suppose we have a PNG image that is placed in the `resources/images` directory in our project. For this tutorial we will use the image sample:
|
||
|
|
||
4 years ago
|
![Sample](sample.png)
|
||
4 years ago
|
|
||
|
```kotlin
|
||
|
import androidx.compose.desktop.Window
|
||
|
import androidx.compose.foundation.Image
|
||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||
|
import androidx.compose.ui.Modifier
|
||
|
import androidx.compose.ui.res.imageResource
|
||
|
|
||
|
fun main() {
|
||
|
Window {
|
||
|
Image(
|
||
|
asset = imageResource("images/sample.png"), // ImageAsset
|
||
|
modifier = Modifier.fillMaxSize()
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
![Resources](image_from_resources.png)
|
||
|
|
||
|
## Loading images from device storage
|
||
|
|
||
4 years ago
|
To create an `ImageAsset` from a loaded image stored in the device memory you can use `org.jetbrains.skija.Image`:
|
||
4 years ago
|
|
||
|
```kotlin
|
||
|
import androidx.compose.desktop.Window
|
||
|
import androidx.compose.foundation.Image
|
||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||
4 years ago
|
import androidx.compose.runtime.Composable
|
||
|
import androidx.compose.runtime.remember
|
||
4 years ago
|
import androidx.compose.ui.graphics.asImageAsset
|
||
|
import androidx.compose.ui.graphics.ImageAsset
|
||
|
import androidx.compose.ui.Modifier
|
||
|
import java.io.File
|
||
|
import org.jetbrains.skija.Image
|
||
|
|
||
|
fun main() {
|
||
|
Window {
|
||
4 years ago
|
val image = remember { imageFromFile(File("sample.png")) }
|
||
4 years ago
|
Image(
|
||
|
asset = image,
|
||
|
modifier = Modifier.fillMaxSize()
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
fun imageFromFile(file: File): ImageAsset {
|
||
|
return Image.makeFromEncoded(file.readBytes()).asImageAsset()
|
||
|
}
|
||
4 years ago
|
```
|
||
|
|
||
|
![Storage](image_from_resources.png)
|
||
|
|
||
4 years ago
|
## Drawing raw image data using native canvas
|
||
4 years ago
|
|
||
4 years ago
|
You may want to draw raw image data, in which case you can use `Canvas` and` drawIntoCanvas`.
|
||
4 years ago
|
|
||
|
```kotlin
|
||
|
import androidx.compose.desktop.Window
|
||
|
import androidx.compose.foundation.Canvas
|
||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||
4 years ago
|
import androidx.compose.runtime.remember
|
||
4 years ago
|
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||
|
import androidx.compose.ui.graphics.nativeCanvas
|
||
|
import androidx.compose.ui.Modifier
|
||
|
import java.awt.image.BufferedImage
|
||
|
import java.io.ByteArrayOutputStream
|
||
|
import java.io.File
|
||
|
import javax.imageio.ImageIO
|
||
|
import org.jetbrains.skija.ColorAlphaType
|
||
|
import org.jetbrains.skija.Bitmap
|
||
|
import org.jetbrains.skija.ImageInfo
|
||
|
import org.jetbrains.skija.IRect
|
||
|
|
||
|
fun main() {
|
||
|
Window {
|
||
4 years ago
|
val bitmap = remember { bitmapFromByteArray() }
|
||
4 years ago
|
Canvas(
|
||
|
modifier = Modifier.fillMaxSize()
|
||
|
) {
|
||
|
drawIntoCanvas { canvas ->
|
||
|
canvas.nativeCanvas.drawBitmapRect(
|
||
|
bitmap,
|
||
|
IRect(0, 0, bitmap.getWidth(), bitmap.getHeight()).toRect()
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fun bitmapFromByteArray(): Bitmap {
|
||
|
var image: BufferedImage? = null
|
||
|
try {
|
||
|
image = ImageIO.read(File("sample.png"))
|
||
|
} catch (e: Exception) {
|
||
|
// image file does not exist
|
||
|
}
|
||
|
|
||
|
if (image == null) {
|
||
|
image = BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB)
|
||
|
}
|
||
|
val pixels = getBytes(image) // assume we only have raw pixels
|
||
|
|
||
|
// allocate and fill skija Bitmap
|
||
|
val bitmap = Bitmap()
|
||
|
bitmap.allocPixels(ImageInfo.makeS32(image.width, image.height, ColorAlphaType.PREMUL))
|
||
|
bitmap.installPixels(bitmap.getImageInfo(), pixels, (image.width * 4).toLong())
|
||
|
|
||
|
return bitmap
|
||
|
}
|
||
|
|
||
|
// creating byte array from BufferedImage
|
||
|
private fun getBytes(image: BufferedImage): ByteArray {
|
||
|
val width = image.width
|
||
|
val height = image.height
|
||
|
|
||
|
val buffer = IntArray(width * height)
|
||
|
image.getRGB(0, 0, width, height, buffer, 0, width)
|
||
|
|
||
|
val pixels = ByteArray(width * height * 4)
|
||
|
|
||
|
var index = 0
|
||
|
for (y in 0 until height) {
|
||
|
for (x in 0 until width) {
|
||
|
val pixel = buffer[y * width + x]
|
||
|
pixels[index++] = ((pixel and 0xFF)).toByte() // Blue component
|
||
|
pixels[index++] = (((pixel shr 8) and 0xFF)).toByte() // Green component
|
||
|
pixels[index++] = (((pixel shr 16) and 0xFF)).toByte() // Red component
|
||
|
pixels[index++] = (((pixel shr 24) and 0xFF)).toByte() // Alpha component
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pixels
|
||
|
}
|
||
|
```
|
||
|
|
||
|
![Drawing raw images](draw_image_into_canvas.png)
|
||
|
|
||
|
## Setting the application window icon
|
||
|
|
||
|
You have 2 ways to set icon for window:
|
||
|
1. Via parameter in `Window` function (or in `AppWindow` constructor)
|
||
|
|
||
|
```kotlin
|
||
|
import androidx.compose.desktop.Window
|
||
|
import androidx.compose.foundation.Image
|
||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||
4 years ago
|
import androidx.compose.runtime.remember
|
||
4 years ago
|
import androidx.compose.ui.graphics.asImageAsset
|
||
|
import androidx.compose.ui.graphics.ImageAsset
|
||
|
import androidx.compose.ui.Modifier
|
||
|
import java.awt.image.BufferedImage
|
||
|
import java.io.ByteArrayOutputStream
|
||
|
import java.io.File
|
||
|
import javax.imageio.ImageIO
|
||
|
import org.jetbrains.skija.Image
|
||
|
|
||
|
fun main() {
|
||
|
val image = getWindowIcon()
|
||
|
Window(
|
||
|
icon = image
|
||
|
) {
|
||
4 years ago
|
val imageAsset = remember { asImageAsset(image) }
|
||
4 years ago
|
Image(
|
||
|
asset = imageAsset,
|
||
|
modifier = Modifier.fillMaxSize()
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fun getWindowIcon(): BufferedImage {
|
||
|
var image: BufferedImage? = null
|
||
|
try {
|
||
|
image = ImageIO.read(File("sample.png"))
|
||
|
} catch (e: Exception) {
|
||
|
// image file does not exist
|
||
|
}
|
||
|
|
||
|
if (image == null) {
|
||
|
image = BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB)
|
||
|
}
|
||
|
|
||
|
return image
|
||
|
}
|
||
|
|
||
|
fun asImageAsset(image: BufferedImage): ImageAsset {
|
||
|
val baos = ByteArrayOutputStream()
|
||
|
ImageIO.write(image, "png", baos)
|
||
|
|
||
|
return Image.makeFromEncoded(baos.toByteArray()).asImageAsset()
|
||
|
}
|
||
|
```
|
||
|
|
||
|
2. Using `setIcon()` method
|
||
|
|
||
|
```kotlin
|
||
|
import androidx.compose.desktop.AppManager
|
||
|
import androidx.compose.desktop.Window
|
||
|
import androidx.compose.foundation.Image
|
||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||
4 years ago
|
import androidx.compose.runtime.remember
|
||
4 years ago
|
import androidx.compose.ui.graphics.asImageAsset
|
||
|
import androidx.compose.ui.graphics.ImageAsset
|
||
|
import androidx.compose.ui.Modifier
|
||
|
import java.awt.image.BufferedImage
|
||
|
import java.io.ByteArrayOutputStream
|
||
|
import java.io.File
|
||
|
import javax.imageio.ImageIO
|
||
|
import org.jetbrains.skija.Image
|
||
|
|
||
|
fun main() {
|
||
|
val image = getWindowIcon()
|
||
|
Window {
|
||
4 years ago
|
val imageAsset = remember { asImageAsset(image) }
|
||
4 years ago
|
Image(
|
||
|
asset = imageAsset,
|
||
|
modifier = Modifier.fillMaxSize()
|
||
|
)
|
||
|
}
|
||
|
|
||
|
val current = AppManager.focusedWindow
|
||
|
if (current != null) {
|
||
|
current.setIcon(image)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fun getWindowIcon(): BufferedImage {
|
||
|
var image: BufferedImage? = null
|
||
|
try {
|
||
|
image = ImageIO.read(File("sample.png"))
|
||
|
} catch (e: Exception) {
|
||
|
// image file does not exist
|
||
|
}
|
||
|
|
||
|
if (image == null) {
|
||
|
image = BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB)
|
||
|
}
|
||
|
|
||
|
return image
|
||
|
}
|
||
|
|
||
|
fun asImageAsset(image: BufferedImage): ImageAsset {
|
||
|
val baos = ByteArrayOutputStream()
|
||
|
ImageIO.write(image, "png", baos)
|
||
|
|
||
|
return Image.makeFromEncoded(baos.toByteArray()).asImageAsset()
|
||
|
}
|
||
|
```
|
||
|
|
||
|
![Window icon](window_icon.png)
|
||
|
|
||
|
## Setting the application tray icon
|
||
|
|
||
4 years ago
|
You can create a tray icon for your application:
|
||
4 years ago
|
|
||
|
```kotlin
|
||
|
import androidx.compose.desktop.AppManager
|
||
|
import androidx.compose.desktop.Window
|
||
|
import androidx.compose.foundation.Image
|
||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||
|
import androidx.compose.runtime.onActive
|
||
|
import androidx.compose.runtime.onDispose
|
||
|
import androidx.compose.ui.graphics.asImageAsset
|
||
|
import androidx.compose.ui.graphics.ImageAsset
|
||
|
import androidx.compose.ui.Modifier
|
||
|
import androidx.compose.ui.window.Tray
|
||
|
import androidx.compose.ui.window.MenuItem
|
||
|
import java.awt.image.BufferedImage
|
||
|
import java.io.ByteArrayOutputStream
|
||
|
import java.io.File
|
||
|
import javax.imageio.ImageIO
|
||
|
import org.jetbrains.skija.Image
|
||
|
|
||
|
fun main() {
|
||
|
val image = getWindowIcon()
|
||
|
Window {
|
||
|
onActive {
|
||
|
val tray = Tray().apply {
|
||
|
icon(getWindowIcon())
|
||
|
menu(
|
||
|
MenuItem(
|
||
|
name = "Quit App",
|
||
|
onClick = { AppManager.exit() }
|
||
|
)
|
||
|
)
|
||
|
}
|
||
|
onDispose {
|
||
|
tray.remove()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
val imageAsset = asImageAsset(image)
|
||
|
Image(
|
||
|
asset = imageAsset,
|
||
|
modifier = Modifier.fillMaxSize()
|
||
|
)
|
||
|
}
|
||
|
|
||
|
val current = AppManager.focusedWindow
|
||
|
if (current != null) {
|
||
|
current.setIcon(image)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fun getWindowIcon(): BufferedImage {
|
||
|
var image: BufferedImage? = null
|
||
|
try {
|
||
|
image = ImageIO.read(File("sample.png"))
|
||
|
} catch (e: Exception) {
|
||
|
// image file does not exist
|
||
|
}
|
||
|
|
||
|
if (image == null) {
|
||
|
image = BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB)
|
||
|
}
|
||
|
|
||
|
return image
|
||
|
}
|
||
|
|
||
|
fun asImageAsset(image: BufferedImage): ImageAsset {
|
||
|
val baos = ByteArrayOutputStream()
|
||
|
ImageIO.write(image, "png", baos)
|
||
|
|
||
|
return Image.makeFromEncoded(baos.toByteArray()).asImageAsset()
|
||
|
}
|
||
|
```
|
||
|
|
||
|
![Tray icon](tray_icon.png)
|