Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
skiko
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
liuqiang
skiko
Commits
10629dff
Unverified
Commit
10629dff
authored
Feb 06, 2026
by
Alexander Maryanovsky
Committed by
GitHub
Feb 06, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Apply `clipComponents` to the layer background (#1159)
parent
a11256b2
Changes
12
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
125 additions
and
27 deletions
+125
-27
SkiaLayer.android.kt
...droidMain/kotlin/org/jetbrains/skiko/SkiaLayer.android.kt
+4
-1
SkiaLayer.awt.kt
...o/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt
+4
-18
SkiaSwingLayer.kt
...wtMain/kotlin/org/jetbrains/skiko/swing/SkiaSwingLayer.kt
+2
-1
SkiaLayerTest.kt
...o/src/awtTest/kotlin/org/jetbrains/skiko/SkiaLayerTest.kt
+62
-0
ClipRectangle.kt
...rc/commonMain/kotlin/org/jetbrains/skiko/ClipRectangle.kt
+10
-0
SkiaLayer.kt
skiko/src/commonMain/kotlin/org/jetbrains/skiko/SkiaLayer.kt
+6
-1
ContextHandler.kt
...Main/kotlin/org/jetbrains/skiko/context/ContextHandler.kt
+22
-2
org_jetbrains_skiko_SkiaLayerTest_layer_background_is_clipped_frame.png
...skiko_SkiaLayerTest_layer_background_is_clipped_frame.png
+0
-0
SkiaLayer.linux.kt
...c/linuxMain/kotlin/org/jetbrains/skiko/SkiaLayer.linux.kt
+3
-1
SkiaLayer.macos.kt
...c/macosMain/kotlin/org/jetbrains/skiko/SkiaLayer.macos.kt
+4
-1
SkiaLayer.uikit.kt
...c/uikitMain/kotlin/org/jetbrains/skiko/SkiaLayer.uikit.kt
+4
-1
SkiaLayer.js.kt
skiko/src/webMain/kotlin/org/jetbrains/skiko/SkiaLayer.js.kt
+4
-1
No files found.
skiko/src/androidMain/kotlin/org/jetbrains/skiko/SkiaLayer.android.kt
View file @
10629dff
...
@@ -29,7 +29,7 @@ actual open class SkiaLayer {
...
@@ -29,7 +29,7 @@ actual open class SkiaLayer {
/**
/**
* The background color of the layer.
* The background color of the layer.
*/
*/
actual
var
backgroundColor
:
Int
=
Color
.
WHITE
actual
internal
var
backgroundColor
:
Int
=
Color
.
WHITE
set
(
value
)
{
set
(
value
)
{
field
=
value
field
=
value
needRender
()
needRender
()
...
@@ -81,5 +81,8 @@ actual open class SkiaLayer {
...
@@ -81,5 +81,8 @@ actual open class SkiaLayer {
actual
val
component
:
Any
?
actual
val
component
:
Any
?
get
()
=
this
.
container
get
()
=
this
.
container
actual
internal
val
cutoutRectangles
:
List
<
ClipRectangle
>
get
()
=
emptyList
()
internal
actual
fun
draw
(
canvas
:
Canvas
):
Unit
=
TODO
()
internal
actual
fun
draw
(
canvas
:
Canvas
):
Unit
=
TODO
()
}
}
\ No newline at end of file
skiko/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt
View file @
10629dff
...
@@ -200,7 +200,7 @@ actual open class SkiaLayer internal constructor(
...
@@ -200,7 +200,7 @@ actual open class SkiaLayer internal constructor(
configureBackground
(
value
,
_background
)
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).
get
()
=
background
.
rgb
// Will return an ancestor's non-null background after setBackground(null).
set
(
value
)
{
set
(
value
)
{
configureBackground
(
_transparency
,
Color
(
value
,
true
))
configureBackground
(
_transparency
,
Color
(
value
,
true
))
...
@@ -335,6 +335,9 @@ actual open class SkiaLayer internal constructor(
...
@@ -335,6 +335,9 @@ actual open class SkiaLayer internal constructor(
val
clipComponents
=
mutableListOf
<
ClipRectangle
>()
val
clipComponents
=
mutableListOf
<
ClipRectangle
>()
internal
actual
val
cutoutRectangles
:
List
<
ClipRectangle
>
get
()
=
clipComponents
@Volatile
@Volatile
private
var
isDisposed
=
false
private
var
isDisposed
=
false
...
@@ -608,12 +611,6 @@ actual open class SkiaLayer internal constructor(
...
@@ -608,12 +611,6 @@ actual open class SkiaLayer internal constructor(
val
pictureRecorder
=
pictureRecorder
!!
val
pictureRecorder
=
pictureRecorder
!!
val
canvas
=
pictureRecorder
.
beginRecording
(
0f
,
0f
,
pictureWidth
,
pictureHeight
)
val
canvas
=
pictureRecorder
.
beginRecording
(
0f
,
0f
,
pictureWidth
,
pictureHeight
)
// clipping
for
(
index
in
clipComponents
.
indices
)
{
val
item
=
clipComponents
[
index
]
canvas
.
clipRectBy
(
item
,
contentScale
)
}
try
{
try
{
isRendering
=
true
isRendering
=
true
renderDelegate
?.
onRender
(
canvas
,
intWidth
,
intHeight
,
nanoTime
)
renderDelegate
?.
onRender
(
canvas
,
intWidth
,
intHeight
,
nanoTime
)
...
@@ -730,17 +727,6 @@ internal fun defaultFPSCounter(
...
@@ -730,17 +727,6 @@ internal fun defaultFPSCounter(
logOnTick
=
true
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
// 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
// issue: https://youtrack.jetbrains.com/issue/CMP-5447/Window-white-line-on-the-bottom-before-resizing
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/swing/SkiaSwingLayer.kt
View file @
10629dff
package
org.jetbrains.skiko.swing
package
org.jetbrains.skiko.swing
import
org.jetbrains.skiko.*
import
org.jetbrains.skiko.*
import
org.jetbrains.skiko.context.cutoutFromClip
import
org.jetbrains.skiko.redrawer.RedrawerManager
import
org.jetbrains.skiko.redrawer.RedrawerManager
import
java.awt.Component
import
java.awt.Component
import
java.awt.Graphics
import
java.awt.Graphics
...
@@ -46,7 +47,7 @@ open class SkiaSwingLayer(
...
@@ -46,7 +47,7 @@ open class SkiaSwingLayer(
// clipping
// clipping
for
(
index
in
clipComponents
.
indices
)
{
for
(
index
in
clipComponents
.
indices
)
{
val
item
=
clipComponents
[
index
]
val
item
=
clipComponents
[
index
]
canvas
.
c
lipRectBy
(
item
,
scale
)
canvas
.
c
utoutFromClip
(
item
,
scale
)
}
}
renderDelegate
.
onRender
(
canvas
,
width
,
height
,
nanoTime
)
renderDelegate
.
onRender
(
canvas
,
width
,
height
,
nanoTime
)
}
}
...
...
skiko/src/awtTest/kotlin/org/jetbrains/skiko/SkiaLayerTest.kt
View file @
10629dff
...
@@ -27,11 +27,13 @@ import org.junit.Rule
...
@@ -27,11 +27,13 @@ import org.junit.Rule
import
org.junit.Test
import
org.junit.Test
import
java.awt.*
import
java.awt.*
import
java.awt.Color
import
java.awt.Color
import
java.awt.Graphics
import
java.awt.Point
import
java.awt.Point
import
java.awt.event.*
import
java.awt.event.*
import
java.util.concurrent.Semaphore
import
java.util.concurrent.Semaphore
import
java.util.concurrent.atomic.AtomicBoolean
import
java.util.concurrent.atomic.AtomicBoolean
import
javax.swing.Box
import
javax.swing.Box
import
javax.swing.JComponent
import
javax.swing.JFrame
import
javax.swing.JFrame
import
javax.swing.JLayeredPane
import
javax.swing.JLayeredPane
import
javax.swing.JPanel
import
javax.swing.JPanel
...
@@ -1348,6 +1350,66 @@ class SkiaLayerTest {
...
@@ -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
class
RectRenderer
(
private
val
getContentScale
:
()
->
Float
,
private
val
getContentScale
:
()
->
Float
,
var
rectWidth
:
Int
,
var
rectWidth
:
Int
,
...
...
skiko/src/commonMain/kotlin/org/jetbrains/skiko/ClipRectangle.kt
View file @
10629dff
...
@@ -9,3 +9,13 @@ interface ClipRectangle {
...
@@ -9,3 +9,13 @@ interface ClipRectangle {
val
width
:
Float
val
width
:
Float
val
height
:
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
skiko/src/commonMain/kotlin/org/jetbrains/skiko/SkiaLayer.kt
View file @
10629dff
...
@@ -36,13 +36,18 @@ expect open class SkiaLayer {
...
@@ -36,13 +36,18 @@ expect open class SkiaLayer {
/**
/**
* The color, in ARGB format, with which the layer is cleared before rendering.
* The color, in ARGB format, with which the layer is cleared before rendering.
*/
*/
var
backgroundColor
:
Int
internal
var
backgroundColor
:
Int
/**
/**
* Underlying platform component.
* Underlying platform component.
*/
*/
val
component
:
Any
?
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.
* Current view used for rendering.
*/
*/
...
...
skiko/src/commonMain/kotlin/org/jetbrains/skiko/context/ContextHandler.kt
View file @
10629dff
...
@@ -41,6 +41,13 @@ internal abstract class ContextHandler(
...
@@ -41,6 +41,13 @@ internal abstract class ContextHandler(
}
}
initCanvas
()
initCanvas
()
canvas
?.
apply
{
canvas
?.
apply
{
clear
(
Color
.
TRANSPARENT
)
val
scale
=
layer
.
contentScale
for
(
clip
in
layer
.
cutoutRectangles
)
{
cutoutFromClip
(
clip
,
scale
)
}
val
layerBg
=
layer
.
backgroundColor
val
layerBg
=
layer
.
backgroundColor
clear
(
clear
(
if
(
layer
.
transparency
&&
isTransparentBackgroundSupported
())
{
if
(
layer
.
transparency
&&
isTransparentBackgroundSupported
())
{
...
@@ -49,6 +56,7 @@ internal abstract class ContextHandler(
...
@@ -49,6 +56,7 @@ internal abstract class ContextHandler(
layerBg
or
0
xFF000000
.
toInt
()
layerBg
or
0
xFF000000
.
toInt
()
}
}
)
)
drawContent
()
drawContent
()
}
}
flush
()
flush
()
...
@@ -56,11 +64,23 @@ internal abstract class ContextHandler(
...
@@ -56,11 +64,23 @@ internal abstract class ContextHandler(
protected
open
fun
isTransparentBackgroundSupported
():
Boolean
{
protected
open
fun
isTransparentBackgroundSupported
():
Boolean
{
if
(
hostOs
==
OS
.
MacOS
)
{
if
(
hostOs
==
OS
.
MacOS
)
{
//
M
acOS transparency is always supported
//
m
acOS transparency is always supported
return
true
return
true
}
}
// for non-
M
acOS in fullscreen transparency is not supported
// for non-
m
acOS in fullscreen transparency is not supported
return
!
layer
.
fullscreen
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
skiko/src/jvmTest/screenshots/org_jetbrains_skiko_SkiaLayerTest_layer_background_is_clipped_frame.png
0 → 100644
View file @
10629dff
901 Bytes
skiko/src/linuxMain/kotlin/org/jetbrains/skiko/SkiaLayer.linux.kt
View file @
10629dff
...
@@ -15,11 +15,13 @@ actual open class SkiaLayer {
...
@@ -15,11 +15,13 @@ actual open class SkiaLayer {
actual
var
transparency
:
Boolean
actual
var
transparency
:
Boolean
get
()
=
TODO
(
"Not yet implemented"
)
get
()
=
TODO
(
"Not yet implemented"
)
set
(
value
)
{}
set
(
value
)
{}
actual
var
backgroundColor
:
Int
internal
actual
var
backgroundColor
:
Int
get
()
=
TODO
(
"Not yet implemented"
)
get
()
=
TODO
(
"Not yet implemented"
)
set
(
value
)
{}
set
(
value
)
{}
actual
val
component
:
Any
?
actual
val
component
:
Any
?
get
()
=
TODO
(
"Not yet implemented"
)
get
()
=
TODO
(
"Not yet implemented"
)
internal
actual
val
cutoutRectangles
:
List
<
ClipRectangle
>
get
()
=
emptyList
()
actual
fun
needRender
(
throttledToVsync
:
Boolean
)
{
actual
fun
needRender
(
throttledToVsync
:
Boolean
)
{
TODO
(
"unimplemented"
)
TODO
(
"unimplemented"
)
}
}
...
...
skiko/src/macosMain/kotlin/org/jetbrains/skiko/SkiaLayer.macos.kt
View file @
10629dff
...
@@ -61,7 +61,7 @@ actual open class SkiaLayer {
...
@@ -61,7 +61,7 @@ actual open class SkiaLayer {
/**
/**
* The background color of the layer.
* The background color of the layer.
*/
*/
actual
var
backgroundColor
:
Int
=
Color
.
WHITE
internal
actual
var
backgroundColor
:
Int
=
Color
.
WHITE
set
(
value
)
{
set
(
value
)
{
field
=
value
field
=
value
needRender
()
needRender
()
...
@@ -76,6 +76,9 @@ actual open class SkiaLayer {
...
@@ -76,6 +76,9 @@ actual open class SkiaLayer {
actual
val
component
:
Any
?
actual
val
component
:
Any
?
get
()
=
this
.
nsView
get
()
=
this
.
nsView
internal
actual
val
cutoutRectangles
:
List
<
ClipRectangle
>
get
()
=
emptyList
()
/**
/**
* Implements rendering logic and events processing.
* Implements rendering logic and events processing.
*/
*/
...
...
skiko/src/uikitMain/kotlin/org/jetbrains/skiko/SkiaLayer.uikit.kt
View file @
10629dff
...
@@ -27,7 +27,7 @@ actual open class SkiaLayer {
...
@@ -27,7 +27,7 @@ actual open class SkiaLayer {
/**
/**
* The background color of the layer, as transparency is not supported.
* 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
)
{
set
(
value
)
{
field
=
value
field
=
value
needRender
()
needRender
()
...
@@ -46,6 +46,9 @@ actual open class SkiaLayer {
...
@@ -46,6 +46,9 @@ actual open class SkiaLayer {
actual
val
component
:
Any
?
actual
val
component
:
Any
?
get
()
=
this
.
view
get
()
=
this
.
view
internal
actual
val
cutoutRectangles
:
List
<
ClipRectangle
>
get
()
=
emptyList
()
val
width
:
Float
val
width
:
Float
get
()
=
view
!!
.
frame
.
useContents
{
get
()
=
view
!!
.
frame
.
useContents
{
return
@useContents
size
.
width
.
toFloat
()
return
@useContents
size
.
width
.
toFloat
()
...
...
skiko/src/webMain/kotlin/org/jetbrains/skiko/SkiaLayer.js.kt
View file @
10629dff
...
@@ -49,7 +49,7 @@ actual open class SkiaLayer {
...
@@ -49,7 +49,7 @@ actual open class SkiaLayer {
/**
/**
* The background color of the layer, as transparency is not supported.
* 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
)
{
set
(
value
)
{
field
=
value
field
=
value
needRender
()
needRender
()
...
@@ -88,6 +88,9 @@ actual open class SkiaLayer {
...
@@ -88,6 +88,9 @@ actual open class SkiaLayer {
actual
val
component
:
Any
?
actual
val
component
:
Any
?
get
()
=
this
.
htmlCanvas
get
()
=
this
.
htmlCanvas
internal
actual
val
cutoutRectangles
:
List
<
ClipRectangle
>
get
()
=
emptyList
()
private
var
htmlCanvas
:
HTMLCanvasElement
?
=
null
private
var
htmlCanvas
:
HTMLCanvasElement
?
=
null
/**
/**
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment