starting with canvas

3
Timo Bryant 2023-12-30 22:53:11 +01:00
parent b4ab91e1db
commit a2483c85d7
5 changed files with 143 additions and 31 deletions

View File

@ -17,6 +17,9 @@ Asset can be found under <path>memento:/mnt/wd/export/data</path>
<def title="Aurora"> <def title="Aurora">
<a href="https://github.com/kirill-grouchnikov/aurora">Building modern, elegant and fast desktop Compose applications</a> <a href="https://github.com/kirill-grouchnikov/aurora">Building modern, elegant and fast desktop Compose applications</a>
</def> </def>
<def title="Zoomimage">
<a href="https://github.com/panpf/zoomimage">Zooming an Image</a>
</def>
</deflist> </deflist>
## Modules - Libraries ## Modules - Libraries

View File

@ -12,4 +12,5 @@ dependencies {
implementation("org.pushing-pixels:aurora-window:1.3.0") implementation("org.pushing-pixels:aurora-window:1.3.0")
implementation(compose.desktop.currentOs) implementation(compose.desktop.currentOs)
implementation("io.github.panpf.zoomimage:zoomimage-compose:1.0.0-beta11") implementation("io.github.panpf.zoomimage:zoomimage-compose:1.0.0-beta11")
implementation("io.github.panpf.zoomimage:zoomimage-compose-desktop:1.0.0-beta11")
} }

View File

@ -1,20 +1,34 @@
package de.itkl.documentViewer package de.itkl.documentViewer
import androidx.compose.foundation.Image import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.*
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.width import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.drawscope.DrawStyle
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.loadImageBitmap import androidx.compose.ui.res.loadImageBitmap
import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.drawText
import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.WindowPlacement import androidx.compose.ui.window.WindowPlacement
@ -23,12 +37,10 @@ import androidx.compose.ui.window.rememberWindowState
import com.github.panpf.zoomimage.ZoomImage import com.github.panpf.zoomimage.ZoomImage
import com.github.panpf.zoomimage.compose.ZoomState import com.github.panpf.zoomimage.compose.ZoomState
import com.github.panpf.zoomimage.compose.rememberZoomState import com.github.panpf.zoomimage.compose.rememberZoomState
import com.github.panpf.zoomimage.compose.subsampling.fromResource import com.github.panpf.zoomimage.compose.zoom.ScrollBarSpec
import com.github.panpf.zoomimage.subsampling.ImageSource import com.github.panpf.zoomimage.compose.zoom.ZoomableState
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.pushingpixels.aurora.component.model.Command
import org.pushingpixels.aurora.component.projection.CommandButtonProjection
import org.pushingpixels.aurora.theming.auroraBackground import org.pushingpixels.aurora.theming.auroraBackground
import org.pushingpixels.aurora.theming.marinerSkin import org.pushingpixels.aurora.theming.marinerSkin
import org.pushingpixels.aurora.window.AuroraWindow import org.pushingpixels.aurora.window.AuroraWindow
@ -36,17 +48,17 @@ import org.pushingpixels.aurora.window.AuroraWindowTitlePaneConfigurations
import org.pushingpixels.aurora.window.auroraApplication import org.pushingpixels.aurora.window.auroraApplication
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.nio.file.Paths import io.github.oshai.kotlinlogging.KotlinLogging
import com.github.panpf.zoomimage.util.Logger as ZoomLogger
class DocumentViewer { private val Log = KotlinLogging.logger { }
}
fun main() = auroraApplication { fun main() = auroraApplication {
val state = rememberWindowState( val state = rememberWindowState(
placement = WindowPlacement.Floating, placement = WindowPlacement.Floating,
position = WindowPosition.Aligned(Alignment.Center), position = WindowPosition.Aligned(Alignment.Center),
size = DpSize(220.dp, 150.dp) size = DpSize(1000. dp, 800.dp)
) )
AuroraWindow( AuroraWindow(
@ -56,31 +68,37 @@ fun main() = auroraApplication {
windowTitlePaneConfiguration = AuroraWindowTitlePaneConfigurations.AuroraPlain(), windowTitlePaneConfiguration = AuroraWindowTitlePaneConfigurations.AuroraPlain(),
onCloseRequest = ::exitApplication onCloseRequest = ::exitApplication
) { ) {
var text by remember { mutableStateOf("Hello, World!") } viewImage()
}
}
Row( @Composable
horizontalArrangement = Arrangement.Center, @Preview
verticalAlignment = Alignment.CenterVertically, fun viewImage() {
Column (
modifier = Modifier.fillMaxSize().auroraBackground() modifier = Modifier.fillMaxSize().auroraBackground()
) { ) {
AsyncImage( val state = rememberZoomState(logger = ZoomLogger("zoom", level = ZoomLogger.INFO))
Text("${state.zoomable.transform.scale} ${state.zoomable.transform.offset}")
Box(
modifier = Modifier.fillMaxSize()
) {
ZoomedImage(
state = state,
load = { loadImageBitmap(File("assets/xs-reg/00001.jpg")) }, load = { loadImageBitmap(File("assets/xs-reg/00001.jpg")) },
painterFor = { remember { BitmapPainter(it) } }, painterFor = { remember { BitmapPainter(it) } },
contentDescription = "Sample", contentDescription = "Sample",
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) )
// CommandButtonProjection( canvas(state.zoomable)
// contentModel = Command( // shapes(state.zoomable)
// text = text,
// action = { text = "Hello, Desktop!" }
// )
// ).project()
} }
} }
} }
@Composable @Composable
fun <T> AsyncImage( fun <T> ZoomedImage(
state: ZoomState,
load: suspend () -> T, load: suspend () -> T,
painterFor: @Composable (T) -> Painter, painterFor: @Composable (T) -> Painter,
contentDescription: String, contentDescription: String,
@ -101,13 +119,101 @@ fun <T> AsyncImage(
} }
if (image != null) { if (image != null) {
val scrollBar = remember {
ScrollBarSpec(
color = Color.Red,
size = 6.dp,
margin = 12.dp,
)
}
ZoomImage( ZoomImage(
painter = painterFor(image!!), painter = painterFor(image!!),
contentDescription = contentDescription, contentDescription = contentDescription,
contentScale = contentScale, contentScale = contentScale,
modifier = modifier modifier = modifier,
scrollBar = scrollBar,
state = state
) )
} }
} }
fun loadImageBitmap(file: File): ImageBitmap = fun loadImageBitmap(file: File): ImageBitmap =
file.inputStream().buffered().use(::loadImageBitmap) file.inputStream().buffered().use(::loadImageBitmap)
data class PointConverter(
val docWidth: Int,
val docHeight: Int,
val canvasWidth: Float,
val canvasHeight: Float
) {
fun convertX(x: Int): Float {
val xf = x.toFloat()
val relXf = docWidth / xf
val scaledXf = canvasWidth * relXf
// println("X: $scaledXf")
return scaledXf
}
fun convertY(y: Int): Float {
val yf = y.toFloat()
val relYf = docHeight / yf
val scaledYf = canvasHeight * relYf
// println("Y: $scaledYf")
return scaledYf
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun shapes(zoomableState: ZoomableState) {
Box(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.offset(100.dp)
.size(100.dp)
.clip(RectangleShape)
.background(Color.Red)
.onClick(true) { println("clicked") }
)
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun canvas(zoomableState: ZoomableState) {
Canvas(modifier = Modifier
.fillMaxSize()
.onPointerEvent(PointerEventType.Move) {
val position = it.changes.first().position
println(position)
}
)
{
val converter = PointConverter(
docWidth = 2481,
docHeight = 3507,
canvasWidth = this.size.width,
canvasHeight = this.size.height
)
// val width = 2481
// val height = 3507
// val x =
// "width": 2481,
// "height": 3507,
// "boundingBox": [
// 288,
// 697,
// 793,
// 700,
// 793,
// 744,
// 288,
// 741
// ],
drawRect(
Color.Blue,
// topLeft = Offset(converter.convertX(288), converter.convertY(697)),
topLeft = zoomableState.transform.offset + Offset(800f,800f),
size = Size(30f, 30f),
style = Stroke(width = 5f)
)
}
}

View File

@ -1,3 +1,4 @@
import gradle.kotlin.dsl.accessors._d9dcfd1a467b0b6fe90c5571a57aa558.api
import org.gradle.api.plugins.jvm.JvmTestSuite import org.gradle.api.plugins.jvm.JvmTestSuite
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
@ -17,6 +18,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
api("io.github.oshai:kotlin-logging-jvm:5.1.0")
testImplementation("io.insert-koin:koin-test:$koin_version") testImplementation("io.insert-koin:koin-test:$koin_version")
} }

View File

@ -24,7 +24,7 @@ class MsOcr: KoinComponent {
contentType(resource.contentType) contentType(resource.contentType)
setBody(resource.read()) setBody(resource.read())
} }
println("got response: ${response.status} in ${response.responseTime}") println(response.bodyAsText())
return response.body() return response.body()
} }
} }