Skip to content

Commit 7d7337d

Browse files
Merge pull request #1 from dt-developers/break-down-server-main-file
Break down the server `Main` file
2 parents d62a95e + 5b2a014 commit 7d7337d

File tree

5 files changed

+163
-136
lines changed

5 files changed

+163
-136
lines changed

zeapp/server/src/main/kotlin/de/berlindroid/zekompanion/server/Main.kt

+10-136
Original file line numberDiff line numberDiff line change
@@ -2,164 +2,38 @@
22

33
package de.berlindroid.zekompanion.server
44

5-
import de.berlindroid.zekompanion.BadgePayload
6-
import de.berlindroid.zekompanion.base64
7-
import de.berlindroid.zekompanion.debase64
8-
import de.berlindroid.zekompanion.ditherFloydSteinberg
9-
import de.berlindroid.zekompanion.grayscale
10-
import de.berlindroid.zekompanion.invert
11-
import de.berlindroid.zekompanion.resize
12-
import de.berlindroid.zekompanion.server.Operation.FloydSteinberg
13-
import de.berlindroid.zekompanion.server.Operation.Grayscale
14-
import de.berlindroid.zekompanion.server.Operation.Invert
15-
import de.berlindroid.zekompanion.server.Operation.Resize
16-
import de.berlindroid.zekompanion.server.Operation.Threshold
17-
import de.berlindroid.zekompanion.threshold
18-
import de.berlindroid.zekompanion.toBinary
19-
import de.berlindroid.zekompanion.zipit
20-
import io.ktor.http.HttpStatusCode
5+
import de.berlindroid.zekompanion.server.routers.imageBin
6+
import de.berlindroid.zekompanion.server.routers.imagePng
7+
import de.berlindroid.zekompanion.server.routers.index
218
import io.ktor.serialization.kotlinx.json.json
22-
import io.ktor.server.application.call
239
import io.ktor.server.application.install
2410
import io.ktor.server.engine.embeddedServer
25-
import io.ktor.server.http.content.CompressedFileType
2611
import io.ktor.server.http.content.staticResources
2712
import io.ktor.server.netty.Netty
2813
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
29-
import io.ktor.server.request.receiveNullable
30-
import io.ktor.server.response.respondText
31-
import io.ktor.server.routing.post
3214
import io.ktor.server.routing.routing
33-
import io.ktor.util.encodeBase64
34-
import kotlinx.serialization.SerialName
35-
import kotlinx.serialization.Serializable
36-
import java.awt.image.BufferedImage
37-
import java.io.ByteArrayInputStream
38-
import java.io.ByteArrayOutputStream
39-
import java.nio.ByteBuffer
40-
import java.nio.IntBuffer
41-
import javax.imageio.ImageIO
4215

43-
@Serializable
44-
sealed class Operation {
45-
@Serializable
46-
@SerialName("FloydSteinberg")
47-
data class FloydSteinberg(val width: Int, val height: Int) : Operation()
16+
private const val DEFAULT_PORT = 8000
4817

49-
@Serializable
50-
@SerialName("Resize")
51-
data class Resize(val width: Int, val height: Int) : Operation()
52-
53-
@Serializable
54-
@SerialName("Threshold")
55-
data class Threshold(val threshold: Int) : Operation()
56-
57-
@Serializable
58-
@SerialName("Invert")
59-
data object Invert : Operation()
60-
61-
@Serializable
62-
@SerialName("Grayscale")
63-
data object Grayscale : Operation()
64-
}
65-
66-
@Serializable
67-
data class ImageRequest(
68-
val operations: List<Operation> = emptyList(),
69-
val image: String = "",
70-
val width: Int = -1,
71-
val height: Int = -1,
72-
)
73-
74-
fun main(args:Array<String>) {
75-
val port = if (args.isNotEmpty()){
18+
fun main(args: Array<String>) {
19+
val port = if (args.isNotEmpty()) {
7620
args.first().toInt()
7721
} else {
78-
8000
22+
DEFAULT_PORT
7923
}
8024
println("🪪Serving on port $port.")
8125

8226
embeddedServer(Netty, port = port) {
8327
install(ContentNegotiation) {
8428
json()
8529
}
86-
8730
routing {
8831
staticResources("/", "static") {
89-
default("static/index.html")
90-
preCompressed(CompressedFileType.GZIP)
32+
index()
9133
}
9234

93-
post("/api/image/bin") {
94-
try {
95-
val image = call.receiveNullable<ImageRequest>() ?: throw IllegalArgumentException("Payload is null")
96-
97-
val payload = BadgePayload(
98-
debug = false,
99-
type = "preview",
100-
meta = "",
101-
payload = image.transform()
102-
.toBinary()
103-
.zipit()
104-
.base64(),
105-
)
106-
107-
call.respondText(payload.toBadgeCommand())
108-
} catch (e: Exception) {
109-
e.printStackTrace()
110-
call.respondText("Error: ${e.message}")
111-
}
112-
}
113-
114-
post("/api/image/png") {
115-
try {
116-
val payload = call.receiveNullable<ImageRequest>() ?: throw IllegalArgumentException("Payload is null")
117-
118-
val image = payload.transform().toImage(payload.width, payload.height)
119-
120-
val stream = ByteArrayOutputStream()
121-
ImageIO.write(image, "png", stream)
122-
123-
call.respondText(stream.toByteArray().encodeBase64())
124-
} catch (e: Exception) {
125-
e.printStackTrace()
126-
call.respondText("Error: ${e.message}", status = HttpStatusCode.MethodNotAllowed)
127-
}
128-
129-
}
35+
imageBin()
36+
imagePng()
13037
}
13138
}.start(wait = true)
13239
}
133-
134-
private fun IntBuffer.toImage(width: Int, height: Int): BufferedImage {
135-
val output = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
136-
output.setRGB(0, 0, width, height, array(), 0, width)
137-
return output
138-
}
139-
140-
private fun ImageRequest.transform(): IntBuffer {
141-
var image = image.debase64().toImage().toPixels()
142-
143-
operations.forEach { operation ->
144-
image = when (operation) {
145-
is FloydSteinberg -> image.ditherFloydSteinberg(operation.width, operation.height)
146-
is Resize -> image.resize(width, height, operation.width, operation.height)
147-
is Threshold -> image.threshold(operation.threshold)
148-
is Invert -> image.invert()
149-
is Grayscale -> image.grayscale()
150-
}
151-
}
152-
return image
153-
}
154-
155-
private fun BufferedImage.toPixels(): IntBuffer {
156-
val output = IntBuffer.allocate(width * height)
157-
getRGB(0, 0, width, height, output.array(), 0, width)
158-
return output
159-
}
160-
161-
private fun ByteBuffer.toImage(): BufferedImage {
162-
val stream = ByteArrayInputStream(array())
163-
val image = ImageIO.read(stream)
164-
return image
165-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package de.berlindroid.zekompanion.server.ext
2+
3+
import de.berlindroid.zekompanion.debase64
4+
import de.berlindroid.zekompanion.ditherFloydSteinberg
5+
import de.berlindroid.zekompanion.grayscale
6+
import de.berlindroid.zekompanion.invert
7+
import de.berlindroid.zekompanion.resize
8+
import de.berlindroid.zekompanion.server.models.ImageRequest
9+
import de.berlindroid.zekompanion.server.models.Operation
10+
import de.berlindroid.zekompanion.threshold
11+
import java.awt.image.BufferedImage
12+
import java.io.ByteArrayInputStream
13+
import java.nio.ByteBuffer
14+
import java.nio.IntBuffer
15+
import javax.imageio.ImageIO
16+
17+
object ImageExt {
18+
private fun BufferedImage.toPixels(): IntBuffer {
19+
val output = IntBuffer.allocate(width * height)
20+
getRGB(0, 0, width, height, output.array(), 0, width)
21+
return output
22+
}
23+
24+
fun ByteBuffer.toImage(): BufferedImage {
25+
val stream = ByteArrayInputStream(array())
26+
val image = ImageIO.read(stream)
27+
return image
28+
}
29+
30+
fun IntBuffer.toImage(width: Int, height: Int): BufferedImage {
31+
val output = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
32+
output.setRGB(0, 0, width, height, array(), 0, width)
33+
return output
34+
}
35+
36+
fun ImageRequest.transform(): IntBuffer {
37+
var image = image.debase64().toImage().toPixels()
38+
39+
operations.forEach { operation ->
40+
image = when (operation) {
41+
is Operation.FloydSteinberg -> image.ditherFloydSteinberg(operation.width, operation.height)
42+
is Operation.Resize -> image.resize(width, height, operation.width, operation.height)
43+
is Operation.Threshold -> image.threshold(operation.threshold)
44+
is Operation.Invert -> image.invert()
45+
is Operation.Grayscale -> image.grayscale()
46+
}
47+
}
48+
return image
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package de.berlindroid.zekompanion.server.models
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
data class ImageRequest(
7+
val operations: List<Operation> = emptyList(),
8+
val image: String = "",
9+
val width: Int = -1,
10+
val height: Int = -1,
11+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package de.berlindroid.zekompanion.server.models
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
sealed class Operation {
8+
@Serializable
9+
@SerialName("FloydSteinberg")
10+
data class FloydSteinberg(val width: Int, val height: Int) : Operation()
11+
12+
@Serializable
13+
@SerialName("Resize")
14+
data class Resize(val width: Int, val height: Int) : Operation()
15+
16+
@Serializable
17+
@SerialName("Threshold")
18+
data class Threshold(val threshold: Int) : Operation()
19+
20+
@Serializable
21+
@SerialName("Invert")
22+
data object Invert : Operation()
23+
24+
@Serializable
25+
@SerialName("Grayscale")
26+
data object Grayscale : Operation()
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package de.berlindroid.zekompanion.server.routers
2+
3+
import de.berlindroid.zekompanion.BadgePayload
4+
import de.berlindroid.zekompanion.base64
5+
import de.berlindroid.zekompanion.server.ext.ImageExt.toImage
6+
import de.berlindroid.zekompanion.server.ext.ImageExt.transform
7+
import de.berlindroid.zekompanion.server.models.ImageRequest
8+
import de.berlindroid.zekompanion.toBinary
9+
import de.berlindroid.zekompanion.zipit
10+
import io.ktor.http.HttpStatusCode
11+
import io.ktor.server.application.call
12+
import io.ktor.server.http.content.CompressedFileType
13+
import io.ktor.server.http.content.StaticContentConfig
14+
import io.ktor.server.request.receiveNullable
15+
import io.ktor.server.response.respondText
16+
import io.ktor.server.routing.Route
17+
import io.ktor.server.routing.post
18+
import io.ktor.util.encodeBase64
19+
import java.io.ByteArrayOutputStream
20+
import java.net.URL
21+
import javax.imageio.ImageIO
22+
23+
fun StaticContentConfig<URL>.index() {
24+
default("static/index.html")
25+
preCompressed(CompressedFileType.GZIP)
26+
}
27+
28+
fun Route.imageBin() =
29+
post("/api/image/bin") {
30+
runCatching {
31+
val image = call.receiveNullable<ImageRequest>() ?: throw IllegalArgumentException("Payload is null")
32+
33+
val payload = BadgePayload(
34+
debug = false,
35+
type = "preview",
36+
meta = "",
37+
payload = image.transform()
38+
.toBinary()
39+
.zipit()
40+
.base64(),
41+
)
42+
43+
call.respondText(payload.toBadgeCommand())
44+
}.onFailure {
45+
it.printStackTrace()
46+
call.respondText("Error: ${it.message}")
47+
}
48+
}
49+
50+
fun Route.imagePng() =
51+
post("/api/image/png") {
52+
runCatching {
53+
val payload = call.receiveNullable<ImageRequest>() ?: throw IllegalArgumentException("Payload is null")
54+
55+
val image = payload.transform().toImage(payload.width, payload.height)
56+
57+
val stream = ByteArrayOutputStream()
58+
ImageIO.write(image, "png", stream)
59+
60+
call.respondText(stream.toByteArray().encodeBase64())
61+
}.onFailure {
62+
it.printStackTrace()
63+
call.respondText("Error: ${it.message}", status = HttpStatusCode.MethodNotAllowed)
64+
}
65+
}

0 commit comments

Comments
 (0)