From 523ff4ae9ce1cd244943e0ef8d7c5d40aec82b3a Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 30 May 2024 13:51:04 +0200 Subject: [PATCH 1/6] Fixed the sample method. It used to go out of bounds and didn't work with odd h values. Issue #750 Made a hot fix for generating an image form the ColormapEditor the image was only halfdrawn. Issue #757 --- .../graphics/scenery/volumes/Colormap.kt | 48 +++++++++++-------- .../graphics/scenery/volumes/ColormapPanel.kt | 9 ++-- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/graphics/scenery/volumes/Colormap.kt b/src/main/kotlin/graphics/scenery/volumes/Colormap.kt index 0c6c003d4..864ff112e 100644 --- a/src/main/kotlin/graphics/scenery/volumes/Colormap.kt +++ b/src/main/kotlin/graphics/scenery/volumes/Colormap.kt @@ -27,30 +27,36 @@ class Colormap(val buffer: ByteBuffer, val width: Int, val height: Int) { private constructor() : this(ByteBuffer.allocate(0), 0, 0) /** - * Returns the value of the colormap, sampled at [position]. + * Returns the value of the color map sampled at the normalized position. + * + * position: A floating point value between 0 and 1. */ @OptIn(ExperimentalUnsignedTypes::class) fun sample(position: Float): Vector4f { - val bufferPosition: Float = position.coerceIn(0.0f, 1.0f) * width - val previous = floor(bufferPosition).roundToInt() - val next = ceil(bufferPosition).roundToInt() - - val globalOffset = width * 4 * height / 2 - val previousColor = globalOffset + previous * 4 - val nextColor = globalOffset + next * 4 - - val b = buffer.duplicate() - val color = ByteArray(8) - @Suppress("USELESS_CAST") - (b.position(previousColor) as? ByteBuffer)?.get(color, 0, 4) - @Suppress("USELESS_CAST") - (b.position(nextColor) as? ByteBuffer)?.get(color, 4, 4) - val ub = color.toUByteArray() - - val c1 = Vector4f(ub[0].toFloat() / 255.0f, ub[1].toFloat() / 255.0f, ub[2].toFloat() / 255.0f, ub[3].toFloat() / 255.0f) - val c2 = Vector4f(ub[4].toFloat() / 255.0f, ub[5].toFloat() / 255.0f, ub[6].toFloat() / 255.0f, ub[7].toFloat() / 255.0f) - - return c1.lerp(c2, bufferPosition - previous.toFloat()) + val bufferPosition: Float = position.coerceIn(0.0f, 1.0f) * (width - 1) + val previous = bufferPosition.toInt() + + val band = height/2 + + //The number of bytes per pixel are fixed at 4. + val globalOffset = width * band * 4 + + val b = buffer.slice() + b.position(globalOffset + previous * 4); + val color = ByteArray(4) + b.get(color); + //Add to "Image" utility class? + val c1 = Vector4f( color[0].toUByte().toFloat() / 255f, color[1].toUByte().toFloat() / 255f, color[2].toUByte().toFloat() / 255f, color[3].toUByte().toFloat() / 255f) + + if( bufferPosition > previous ){ + //interpolate fraction part. + b.get(color); + val c2 = Vector4f( color[0].toUByte().toFloat() / 255f, color[1].toUByte().toFloat() / 255f, color[2].toUByte().toFloat() / 255f, color[3].toUByte().toFloat() / 255f) + return c1.lerp(c2, bufferPosition - previous.toFloat()) + } else{ + return c1 + } + } companion object { diff --git a/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt b/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt index 03e760ba3..da682a614 100644 --- a/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt +++ b/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt @@ -304,8 +304,8 @@ class ColormapPanel(val target:Volume?): JPanel() { internal fun toImage(): BufferedImage { val rec: Rectangle = this.bounds - val bufferedImage = BufferedImage(rec.width, rec.height, BufferedImage.TYPE_INT_ARGB) - paintBackgroundGradient(colorPoints.sortedBy { it.position }, bufferedImage.graphics as Graphics2D) + val bufferedImage = BufferedImage(rec.width, rec.height - 10, BufferedImage.TYPE_INT_ARGB) + paintBackgroundGradient(colorPoints.sortedBy { it.position }, bufferedImage.createGraphics()) return bufferedImage } @@ -324,8 +324,11 @@ class ColormapPanel(val target:Volume?): JPanel() { val h = height val pointList = colorPoints.sortedBy { it.position } + // background Gradient - paintBackgroundGradient(pointList, g2d) + //paintBackgroundGradient(pointList, g2d) + val img = toImage() + g2d.drawImage(img, 0, 0, this) // color point markers val relativeSize = 0.25f //relative to height From 2d413df712d1ffb5bb6844f48b900eaf1e90bb08 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 30 May 2024 22:42:00 +0200 Subject: [PATCH 2/6] switched slice back to duplicate. Calling slice without any arguments will slice from the current position. Which might not be zero. --- src/main/kotlin/graphics/scenery/volumes/Colormap.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/graphics/scenery/volumes/Colormap.kt b/src/main/kotlin/graphics/scenery/volumes/Colormap.kt index 864ff112e..f7934f7bc 100644 --- a/src/main/kotlin/graphics/scenery/volumes/Colormap.kt +++ b/src/main/kotlin/graphics/scenery/volumes/Colormap.kt @@ -41,7 +41,7 @@ class Colormap(val buffer: ByteBuffer, val width: Int, val height: Int) { //The number of bytes per pixel are fixed at 4. val globalOffset = width * band * 4 - val b = buffer.slice() + val b = buffer.duplicate() b.position(globalOffset + previous * 4); val color = ByteArray(4) b.get(color); From 005496347df98e3c5e6f16f80924ea4cc149b09c Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 13 Jun 2024 09:16:46 +0200 Subject: [PATCH 3/6] Style updates --- src/main/kotlin/graphics/scenery/volumes/Colormap.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/graphics/scenery/volumes/Colormap.kt b/src/main/kotlin/graphics/scenery/volumes/Colormap.kt index f7934f7bc..abf62b50e 100644 --- a/src/main/kotlin/graphics/scenery/volumes/Colormap.kt +++ b/src/main/kotlin/graphics/scenery/volumes/Colormap.kt @@ -44,16 +44,16 @@ class Colormap(val buffer: ByteBuffer, val width: Int, val height: Int) { val b = buffer.duplicate() b.position(globalOffset + previous * 4); val color = ByteArray(4) - b.get(color); + b.get(color) //Add to "Image" utility class? - val c1 = Vector4f( color[0].toUByte().toFloat() / 255f, color[1].toUByte().toFloat() / 255f, color[2].toUByte().toFloat() / 255f, color[3].toUByte().toFloat() / 255f) + val c1 = Vector4f(color[0].toUByte().toFloat() / 255f, color[1].toUByte().toFloat() / 255f, color[2].toUByte().toFloat() / 255f, color[3].toUByte().toFloat() / 255f) - if( bufferPosition > previous ){ + if (bufferPosition > previous){ //interpolate fraction part. b.get(color); val c2 = Vector4f( color[0].toUByte().toFloat() / 255f, color[1].toUByte().toFloat() / 255f, color[2].toUByte().toFloat() / 255f, color[3].toUByte().toFloat() / 255f) return c1.lerp(c2, bufferPosition - previous.toFloat()) - } else{ + } else { return c1 } From e4428058d604aeac75592e446f0d4d2fb7e1b044 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 13 Jun 2024 09:40:28 +0200 Subject: [PATCH 4/6] The image displayed is the height of the jpanel - the space for the markers. This creates a variable 'markerSpace' to show where the magic -10 appears. --- .../graphics/scenery/volumes/ColormapPanel.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt b/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt index da682a614..e99200533 100644 --- a/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt +++ b/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt @@ -208,7 +208,8 @@ class ColormapPanel(val target:Volume?): JPanel() { private var hoveredOver: ColorPoint? = null private var dragging: ColorPoint? = null private var dragged = false - + private var markerSpace = 10 + init { this.layout = MigLayout() this.preferredSize = Dimension(1000, 40) @@ -304,7 +305,7 @@ class ColormapPanel(val target:Volume?): JPanel() { internal fun toImage(): BufferedImage { val rec: Rectangle = this.bounds - val bufferedImage = BufferedImage(rec.width, rec.height - 10, BufferedImage.TYPE_INT_ARGB) + val bufferedImage = BufferedImage(rec.width, rec.height - markerSpace, BufferedImage.TYPE_INT_ARGB) paintBackgroundGradient(colorPoints.sortedBy { it.position }, bufferedImage.createGraphics()) return bufferedImage } @@ -353,10 +354,10 @@ class ColormapPanel(val target:Volume?): JPanel() { // This draws a triangle below the gradient bar to indicate control points g2d.paint = outlineColor g2d.drawPolygon(intArrayOf(p1x.toInt(), (p1x - innerSize).toInt(), (p1x + innerSize).toInt()), - intArrayOf(h-10, h-1, h-1), 3) + intArrayOf(h - markerSpace, h - 1, h - 1), 3) g2d.paint = it.color - g2d.fillPolygon(intArrayOf(p1x.toInt(), (p1x - innerSize-1).toInt(), (p1x + innerSize+1).toInt()), - intArrayOf(h-10-1, h, h), 3) + g2d.fillPolygon(intArrayOf(p1x.toInt(), (p1x - innerSize - 1).toInt(), (p1x + innerSize + 1).toInt()), + intArrayOf(h - markerSpace - 1, h, h), 3) } } @@ -365,7 +366,7 @@ class ColormapPanel(val target:Volume?): JPanel() { g2d: Graphics2D ) { val w = width - val h = height + val h = height - markerSpace for (i in 0 until pointList.size - 1) { val p1 = pointList[i] val p2 = pointList[i + 1] @@ -374,7 +375,7 @@ class ColormapPanel(val target:Volume?): JPanel() { val gp = GradientPaint(p1x, 0f, p1.color, p2x, 0f, p2.color) g2d.paint = gp - g2d.fillRect(p1x.toInt(), 0, p2x.toInt(), h-10) + g2d.fillRect(p1x.toInt(), 0, p2x.toInt(), h) } } From 99b96b451efb33a335a2aec055d79893fe7ad4a0 Mon Sep 17 00:00:00 2001 From: Ulrik Guenther Date: Wed, 7 Aug 2024 13:24:32 +0200 Subject: [PATCH 5/6] Colormap: Fix formatting and add utility function for creating Vector4f-based RGBA colors --- .../graphics/scenery/volumes/Colormap.kt | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/graphics/scenery/volumes/Colormap.kt b/src/main/kotlin/graphics/scenery/volumes/Colormap.kt index abf62b50e..6a1f84bfa 100644 --- a/src/main/kotlin/graphics/scenery/volumes/Colormap.kt +++ b/src/main/kotlin/graphics/scenery/volumes/Colormap.kt @@ -26,37 +26,42 @@ class Colormap(val buffer: ByteBuffer, val width: Int, val height: Int) { @Suppress("unused") private constructor() : this(ByteBuffer.allocate(0), 0, 0) + private fun ByteArray.toRGBAColor() = Vector4f( + this[0].toUByte().toFloat() / 255f, + this[1].toUByte().toFloat() / 255f, + this[2].toUByte().toFloat() / 255f, + this[3].toUByte().toFloat() / 255f + ) + /** * Returns the value of the color map sampled at the normalized position. * * position: A floating point value between 0 and 1. */ - @OptIn(ExperimentalUnsignedTypes::class) fun sample(position: Float): Vector4f { val bufferPosition: Float = position.coerceIn(0.0f, 1.0f) * (width - 1) val previous = bufferPosition.toInt() val band = height/2 - //The number of bytes per pixel are fixed at 4. + // The number of bytes per pixel are fixed at 4. val globalOffset = width * band * 4 val b = buffer.duplicate() - b.position(globalOffset + previous * 4); + b.position(globalOffset + previous * 4) val color = ByteArray(4) b.get(color) - //Add to "Image" utility class? - val c1 = Vector4f(color[0].toUByte().toFloat() / 255f, color[1].toUByte().toFloat() / 255f, color[2].toUByte().toFloat() / 255f, color[3].toUByte().toFloat() / 255f) + // Add to "Image" utility class? + val c1 = color.toRGBAColor() - if (bufferPosition > previous){ + if(bufferPosition > previous) { //interpolate fraction part. - b.get(color); - val c2 = Vector4f( color[0].toUByte().toFloat() / 255f, color[1].toUByte().toFloat() / 255f, color[2].toUByte().toFloat() / 255f, color[3].toUByte().toFloat() / 255f) + b.get(color) + val c2 = color.toRGBAColor() return c1.lerp(c2, bufferPosition - previous.toFloat()) } else { return c1 } - } companion object { From 5fc60bfc087c420645d2dcce1eac61e94a29a822 Mon Sep 17 00:00:00 2001 From: Ulrik Guenther Date: Wed, 7 Aug 2024 13:35:48 +0200 Subject: [PATCH 6/6] ColormapPanel: Use correct texture size for padded colormap --- .../kotlin/graphics/scenery/volumes/ColormapPanel.kt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt b/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt index e99200533..cbb980e6d 100644 --- a/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt +++ b/src/main/kotlin/graphics/scenery/volumes/ColormapPanel.kt @@ -299,7 +299,8 @@ class ColormapPanel(val target:Volume?): JPanel() { repaint() if(width > 0 && height > 0) { - target?.colormap = Colormap.fromBuffer(toBuffer(), width, height) + val bi = toImage() + target?.colormap = Colormap.fromBuffer(Image.bufferedImageToRGBABuffer(bi), bi.width, bi.height) } } @@ -310,10 +311,6 @@ class ColormapPanel(val target:Volume?): JPanel() { return bufferedImage } - private fun toBuffer(): ByteBuffer { - return Image.bufferedImageToRGBABuffer(toImage()) - } - private fun pointAtMouse(e: MouseEvent) = colorPoints.firstOrNull { (e.x - (width * it.position)).absoluteValue < height / 2 } @@ -325,9 +322,7 @@ class ColormapPanel(val target:Volume?): JPanel() { val h = height val pointList = colorPoints.sortedBy { it.position } - // background Gradient - //paintBackgroundGradient(pointList, g2d) val img = toImage() g2d.drawImage(img, 0, 0, this)