Unverified Commit 10629dff authored by Alexander Maryanovsky's avatar Alexander Maryanovsky Committed by GitHub

Apply `clipComponents` to the layer background (#1159)

parent a11256b2
......@@ -29,7 +29,7 @@ actual open class SkiaLayer {
/**
* The background color of the layer.
*/
actual var backgroundColor: Int = Color.WHITE
actual internal var backgroundColor: Int = Color.WHITE
set(value) {
field = value
needRender()
......@@ -81,5 +81,8 @@ actual open class SkiaLayer {
actual val component: Any?
get() = this.container
actual internal val cutoutRectangles: List<ClipRectangle>
get() = emptyList()
internal actual fun draw(canvas: Canvas): Unit = TODO()
}
\ No newline at end of file
......@@ -200,7 +200,7 @@ actual open class SkiaLayer internal constructor(
configureBackground(value, _background)
}
actual var backgroundColor: Int
internal actual var backgroundColor: Int
get() = background.rgb // Will return an ancestor's non-null background after setBackground(null).
set(value) {
configureBackground(_transparency, Color(value, true))
......@@ -335,6 +335,9 @@ actual open class SkiaLayer internal constructor(
val clipComponents = mutableListOf<ClipRectangle>()
internal actual val cutoutRectangles: List<ClipRectangle>
get() = clipComponents
@Volatile
private var isDisposed = false
......@@ -608,12 +611,6 @@ actual open class SkiaLayer internal constructor(
val pictureRecorder = pictureRecorder!!
val canvas = pictureRecorder.beginRecording(0f, 0f, pictureWidth, pictureHeight)
// clipping
for (index in clipComponents.indices) {
val item = clipComponents[index]
canvas.clipRectBy(item, contentScale)
}
try {
isRendering = true
renderDelegate?.onRender(canvas, intWidth, intHeight, nanoTime)
......@@ -730,17 +727,6 @@ internal fun defaultFPSCounter(
logOnTick = true
)
}
@Suppress("NOTHING_TO_INLINE")
internal inline fun Canvas.clipRectBy(rectangle: ClipRectangle, scale: Float) {
clipRect(
left = rectangle.x * scale,
top = rectangle.y * scale,
right = (rectangle.x + rectangle.width) * scale,
bottom = (rectangle.y + rectangle.height) * scale,
mode = ClipMode.DIFFERENCE,
antiAlias = true
)
}
// TODO Recheck this method validity in 2 cases - full Window content, and a Panel content
// issue: https://youtrack.jetbrains.com/issue/CMP-5447/Window-white-line-on-the-bottom-before-resizing
......
package org.jetbrains.skiko.swing
import org.jetbrains.skiko.*
import org.jetbrains.skiko.context.cutoutFromClip
import org.jetbrains.skiko.redrawer.RedrawerManager
import java.awt.Component
import java.awt.Graphics
......@@ -46,7 +47,7 @@ open class SkiaSwingLayer(
// clipping
for (index in clipComponents.indices) {
val item = clipComponents[index]
canvas.clipRectBy(item, scale)
canvas.cutoutFromClip(item, scale)
}
renderDelegate.onRender(canvas, width, height, nanoTime)
}
......
......@@ -27,11 +27,13 @@ import org.junit.Rule
import org.junit.Test
import java.awt.*
import java.awt.Color
import java.awt.Graphics
import java.awt.Point
import java.awt.event.*
import java.util.concurrent.Semaphore
import java.util.concurrent.atomic.AtomicBoolean
import javax.swing.Box
import javax.swing.JComponent
import javax.swing.JFrame
import javax.swing.JLayeredPane
import javax.swing.JPanel
......@@ -1348,6 +1350,66 @@ class SkiaLayerTest {
}
}
@Test
fun `layer background is clipped`() = uiTest {
val window = JFrame()
try {
// Simulate how Swing Interop in Compose for Desktop works
val layeredPane = JLayeredPane()
layeredPane.layout = null
val swingComponent = object : JComponent() {
override fun paint(g: Graphics) {
g.color = Color.GREEN
g.fillRect(0, 0, width, height)
}
}
val layer = SkiaLayer(
properties = SkiaLayerProperties(renderApi = renderApi),
)
layer.bounds = Rectangle(0, 0, 300, 300)
layer.background = Color.YELLOW
val layerContentPaint = Paint().also { it.color = Color.RED.rgb }
layer.renderDelegate = SkikoRenderDelegate { canvas, width, height, _ ->
canvas.drawRect(Rect(0f, 0f, width.toFloat(), height/3f), layerContentPaint)
}
swingComponent.bounds = Rectangle(0, 200, 300, 100)
layer.clipComponents.add(
ClipRectangle(
x = swingComponent.x.toFloat(),
y = swingComponent.y.toFloat(),
width = swingComponent.width.toFloat(),
height = swingComponent.height.toFloat()
)
)
layeredPane.add(layer, BorderLayout.CENTER)
layeredPane.add(swingComponent, BorderLayout.CENTER, 0)
window.contentPane.layout = BorderLayout()
window.contentPane.add(layeredPane, BorderLayout.CENTER)
window.setLocation(200, 200)
window.size = layer.size
window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
window.isUndecorated = true
window.isVisible = true
withContext(Dispatchers.Default) {
Robot().waitForIdle()
}
// Expect to see three layers:
// - Red, from the layer content
// - Yellow, from the layer background
// - Green, from the Swing component
screenshots.assert(window.bounds, "frame")
} finally {
window.close()
}
}
private class RectRenderer(
private val getContentScale: () -> Float,
var rectWidth: Int,
......
......@@ -9,3 +9,13 @@ interface ClipRectangle {
val width: Float
val height: Float
}
/**
* Returns a [ClipRectangle] with the specified values.
*/
internal fun ClipRectangle(x: Float, y: Float, width: Float, height: Float) = object : ClipRectangle {
override val x: Float = x
override val y: Float = y
override val width: Float = width
override val height: Float = height
}
\ No newline at end of file
......@@ -36,13 +36,18 @@ expect open class SkiaLayer {
/**
* The color, in ARGB format, with which the layer is cleared before rendering.
*/
var backgroundColor: Int
internal var backgroundColor: Int
/**
* Underlying platform component.
*/
val component: Any?
/**
* A list of rectangles to cut out from the rendered content; No content will be drawn inside them.
*/
internal val cutoutRectangles: List<ClipRectangle>
/**
* Current view used for rendering.
*/
......
......@@ -41,6 +41,13 @@ internal abstract class ContextHandler(
}
initCanvas()
canvas?.apply {
clear(Color.TRANSPARENT)
val scale = layer.contentScale
for (clip in layer.cutoutRectangles) {
cutoutFromClip(clip, scale)
}
val layerBg = layer.backgroundColor
clear(
if (layer.transparency && isTransparentBackgroundSupported()) {
......@@ -49,6 +56,7 @@ internal abstract class ContextHandler(
layerBg or 0xFF000000.toInt()
}
)
drawContent()
}
flush()
......@@ -56,11 +64,23 @@ internal abstract class ContextHandler(
protected open fun isTransparentBackgroundSupported(): Boolean {
if (hostOs == OS.MacOS) {
// MacOS transparency is always supported
// macOS transparency is always supported
return true
}
// for non-MacOS in fullscreen transparency is not supported
// for non-macOS in fullscreen transparency is not supported
return !layer.fullscreen
}
}
@Suppress("NOTHING_TO_INLINE")
internal inline fun Canvas.cutoutFromClip(rectangle: ClipRectangle, scale: Float) {
clipRect(
left = rectangle.x * scale,
top = rectangle.y * scale,
right = (rectangle.x + rectangle.width) * scale,
bottom = (rectangle.y + rectangle.height) * scale,
mode = ClipMode.DIFFERENCE,
antiAlias = true
)
}
\ No newline at end of file
......@@ -15,11 +15,13 @@ actual open class SkiaLayer {
actual var transparency: Boolean
get() = TODO("Not yet implemented")
set(value) {}
actual var backgroundColor: Int
internal actual var backgroundColor: Int
get() = TODO("Not yet implemented")
set(value) {}
actual val component: Any?
get() = TODO("Not yet implemented")
internal actual val cutoutRectangles: List<ClipRectangle>
get() = emptyList()
actual fun needRender(throttledToVsync: Boolean) {
TODO("unimplemented")
}
......
......@@ -61,7 +61,7 @@ actual open class SkiaLayer {
/**
* The background color of the layer.
*/
actual var backgroundColor: Int = Color.WHITE
internal actual var backgroundColor: Int = Color.WHITE
set(value) {
field = value
needRender()
......@@ -76,6 +76,9 @@ actual open class SkiaLayer {
actual val component: Any?
get() = this.nsView
internal actual val cutoutRectangles: List<ClipRectangle>
get() = emptyList()
/**
* Implements rendering logic and events processing.
*/
......
......@@ -27,7 +27,7 @@ actual open class SkiaLayer {
/**
* The background color of the layer, as transparency is not supported.
*/
actual var backgroundColor: Int = Color.WHITE
internal actual var backgroundColor: Int = Color.WHITE
set(value) {
field = value
needRender()
......@@ -46,6 +46,9 @@ actual open class SkiaLayer {
actual val component: Any?
get() = this.view
internal actual val cutoutRectangles: List<ClipRectangle>
get() = emptyList()
val width: Float
get() = view!!.frame.useContents {
return@useContents size.width.toFloat()
......
......@@ -49,7 +49,7 @@ actual open class SkiaLayer {
/**
* The background color of the layer, as transparency is not supported.
*/
actual var backgroundColor: Int = Color.WHITE
internal actual var backgroundColor: Int = Color.WHITE
set(value) {
field = value
needRender()
......@@ -88,6 +88,9 @@ actual open class SkiaLayer {
actual val component: Any?
get() = this.htmlCanvas
internal actual val cutoutRectangles: List<ClipRectangle>
get() = emptyList()
private var htmlCanvas: HTMLCanvasElement? = null
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment