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
f30f9eee
Unverified
Commit
f30f9eee
authored
Feb 05, 2026
by
Alexander Maryanovsky
Committed by
GitHub
Feb 05, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Convert custom SkikoRenderDelegate instances to lambdas (#1160)
parent
faca8b45
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
50 additions
and
78 deletions
+50
-78
SkiaSwingLayer.kt
...wtMain/kotlin/org/jetbrains/skiko/swing/SkiaSwingLayer.kt
+7
-10
SkiaLayerTest.kt
...o/src/awtTest/kotlin/org/jetbrains/skiko/SkiaLayerTest.kt
+43
-68
No files found.
skiko/src/awtMain/kotlin/org/jetbrains/skiko/swing/SkiaSwingLayer.kt
View file @
f30f9eee
package
org.jetbrains.skiko.swing
package
org.jetbrains.skiko.swing
import
org.jetbrains.skia.Canvas
import
org.jetbrains.skiko.*
import
org.jetbrains.skiko.*
import
org.jetbrains.skiko.redrawer.RedrawerManager
import
org.jetbrains.skiko.redrawer.RedrawerManager
import
java.awt.Component
import
java.awt.Component
...
@@ -42,16 +41,14 @@ open class SkiaSwingLayer(
...
@@ -42,16 +41,14 @@ open class SkiaSwingLayer(
val
clipComponents
:
MutableList
<
ClipRectangle
>
get
()
=
mutableListOf
()
val
clipComponents
:
MutableList
<
ClipRectangle
>
get
()
=
mutableListOf
()
private
val
renderDelegateWithClipping
=
object
:
SkikoRenderDelegate
by
renderDelegate
{
private
val
renderDelegateWithClipping
=
SkikoRenderDelegate
{
canvas
,
width
,
height
,
nanoTime
->
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
val
scale
=
graphicsConfiguration
.
defaultTransform
.
scaleX
.
toFloat
()
val
scale
=
graphicsConfiguration
.
defaultTransform
.
scaleX
.
toFloat
()
// clipping
// clipping
for
(
index
in
clipComponents
.
indices
)
{
for
(
index
in
clipComponents
.
indices
)
{
val
item
=
clipComponents
[
index
]
val
item
=
clipComponents
[
index
]
canvas
.
clipRectBy
(
item
,
scale
)
canvas
.
clipRectBy
(
item
,
scale
)
}
renderDelegate
.
onRender
(
canvas
,
width
,
height
,
nanoTime
)
}
}
renderDelegate
.
onRender
(
canvas
,
width
,
height
,
nanoTime
)
}
}
private
val
swingLayerProperties
=
object
:
SwingLayerProperties
{
private
val
swingLayerProperties
=
object
:
SwingLayerProperties
{
...
...
skiko/src/awtTest/kotlin/org/jetbrains/skiko/SkiaLayerTest.kt
View file @
f30f9eee
...
@@ -88,17 +88,15 @@ class SkiaLayerTest {
...
@@ -88,17 +88,15 @@ class SkiaLayerTest {
window
.
setLocation
(
200
,
200
)
window
.
setLocation
(
200
,
200
)
window
.
setSize
(
400
,
600
)
window
.
setSize
(
400
,
600
)
window
.
defaultCloseOperation
=
WindowConstants
.
EXIT_ON_CLOSE
window
.
defaultCloseOperation
=
WindowConstants
.
EXIT_ON_CLOSE
window
.
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
window
.
layer
.
renderDelegate
=
SkikoRenderDelegate
{
canvas
,
width
,
height
,
_
->
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
val
c1
=
counter1
val
c1
=
counter1
val
c2
=
counter2
val
c2
=
counter2
paint
.
color
=
colors
[
c1
.
mod
(
colors
.
size
)].
rgb
paint
.
color
=
colors
[
c1
.
mod
(
colors
.
size
)].
rgb
canvas
.
drawRect
(
Rect
(
0f
,
0f
,
width
.
toFloat
(),
height
/
2f
),
paint
)
canvas
.
drawRect
(
Rect
(
0f
,
0f
,
width
.
toFloat
(),
height
/
2f
),
paint
)
paint
.
color
=
colors
[
c2
.
mod
(
colors
.
size
)].
rgb
paint
.
color
=
colors
[
c2
.
mod
(
colors
.
size
)].
rgb
canvas
.
drawRect
(
Rect
(
0f
,
height
/
2f
,
width
.
toFloat
(),
height
.
toFloat
()),
paint
)
canvas
.
drawRect
(
Rect
(
0f
,
height
/
2f
,
width
.
toFloat
(),
height
.
toFloat
()),
paint
)
}
}
}
window
.
isVisible
=
true
window
.
isVisible
=
true
...
@@ -255,11 +253,7 @@ class SkiaLayerTest {
...
@@ -255,11 +253,7 @@ class SkiaLayerTest {
properties
=
SkiaLayerProperties
(
renderApi
=
renderApi
)
properties
=
SkiaLayerProperties
(
renderApi
=
renderApi
)
)
)
var
renderedWidth
=
-
1
var
renderedWidth
=
-
1
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
layer
.
renderDelegate
=
SkikoRenderDelegate
{
_
,
width
,
_
,
_
->
renderedWidth
=
width
}
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
renderedWidth
=
width
}
}
layer
.
size
=
Dimension
(
0
,
0
)
layer
.
size
=
Dimension
(
0
,
0
)
val
density
=
window
.
graphicsConfiguration
.
defaultTransform
.
scaleX
val
density
=
window
.
graphicsConfiguration
.
defaultTransform
.
scaleX
try
{
try
{
...
@@ -309,12 +303,11 @@ class SkiaLayerTest {
...
@@ -309,12 +303,11 @@ class SkiaLayerTest {
properties
=
SkiaLayerProperties
(
renderApi
=
renderApi
)
properties
=
SkiaLayerProperties
(
renderApi
=
renderApi
)
)
)
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
layer
.
renderDelegate
=
SkikoRenderDelegate
{
canvas
,
width
,
height
,
_
->
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
canvas
.
drawRect
(
canvas
.
drawRect
(
Rect
(
0f
,
0f
,
width
.
toFloat
(),
height
.
toFloat
()),
Paint
().
apply
{
r
=
Rect
(
0f
,
0f
,
width
.
toFloat
(),
height
.
toFloat
()),
color
=
Color
.
RED
.
rgb
paint
=
Paint
().
apply
{
color
=
Color
.
RED
.
rgb
}
})
)
}
}
}
layer
.
size
=
Dimension
(
100
,
100
)
layer
.
size
=
Dimension
(
100
,
100
)
val
box
=
Box
.
createVerticalBox
().
apply
{
val
box
=
Box
.
createVerticalBox
().
apply
{
...
@@ -416,7 +409,7 @@ class SkiaLayerTest {
...
@@ -416,7 +409,7 @@ class SkiaLayerTest {
window
.
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
window
.
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
window
.
layer
.
fullscreen
=
true
window
.
layer
.
fullscreen
=
true
var
stateRemainsFullscreen
=
true
var
stateRemainsFullscreen
=
true
window
.
addComponentListener
(
object
:
ComponentAdapter
(){
window
.
addComponentListener
(
object
:
ComponentAdapter
()
{
override
fun
componentResized
(
e
:
ComponentEvent
?)
{
override
fun
componentResized
(
e
:
ComponentEvent
?)
{
if
(!
window
.
layer
.
fullscreen
)
if
(!
window
.
layer
.
fullscreen
)
stateRemainsFullscreen
=
false
stateRemainsFullscreen
=
false
...
@@ -430,7 +423,7 @@ class SkiaLayerTest {
...
@@ -430,7 +423,7 @@ class SkiaLayerTest {
}
finally
{
}
finally
{
window
.
close
()
window
.
close
()
// Delay before starting
next test to let the window animation to complete,
and allow the next window
// Delay before starting
the next test to let the window animation complete
and allow the next window
// to become fullscreen
// to become fullscreen
if
(
hostOs
==
OS
.
MacOS
)
{
if
(
hostOs
==
OS
.
MacOS
)
{
delay
(
1000
)
delay
(
1000
)
...
@@ -447,11 +440,7 @@ class SkiaLayerTest {
...
@@ -447,11 +440,7 @@ class SkiaLayerTest {
window
.
setLocation
(
200
,
200
)
window
.
setLocation
(
200
,
200
)
window
.
setSize
(
40
,
20
)
window
.
setSize
(
40
,
20
)
window
.
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
window
.
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
window
.
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
window
.
layer
.
renderDelegate
=
SkikoRenderDelegate
{
_
,
_
,
_
,
_
->
renderCount
++
}
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
renderCount
++
}
}
window
.
isUndecorated
=
true
window
.
isUndecorated
=
true
window
.
isVisible
=
true
window
.
isVisible
=
true
...
@@ -497,7 +486,7 @@ class SkiaLayerTest {
...
@@ -497,7 +486,7 @@ class SkiaLayerTest {
if
(
needOpen
)
{
if
(
needOpen
)
{
val
window
=
window
(
isAnimated
=
random
.
nextDouble
()
>
0.5f
)
val
window
=
window
(
isAnimated
=
random
.
nextDouble
()
>
0.5f
)
openedWindows
.
add
(
window
)
openedWindows
.
add
(
window
)
}
else
if
(
openedWindows
.
size
>
0
)
{
}
else
if
(
openedWindows
.
isNotEmpty
()
)
{
val
index
=
(
random
.
nextDouble
()
*
(
openedWindows
.
size
-
1
)).
toInt
()
val
index
=
(
random
.
nextDouble
()
*
(
openedWindows
.
size
-
1
)).
toInt
()
openedWindows
.
removeAt
(
index
).
close
()
openedWindows
.
removeAt
(
index
).
close
()
}
}
...
@@ -551,10 +540,7 @@ class SkiaLayerTest {
...
@@ -551,10 +540,7 @@ class SkiaLayerTest {
setSize
(
400
,
200
)
setSize
(
400
,
200
)
preferredSize
=
Dimension
(
400
,
200
)
preferredSize
=
Dimension
(
400
,
200
)
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
layer
.
renderDelegate
=
SkikoRenderDelegate
{
_
,
_
,
_
,
_
->
}
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
}
}
}
}
repeat
(
30
)
{
repeat
(
30
)
{
...
@@ -701,15 +687,13 @@ class SkiaLayerTest {
...
@@ -701,15 +687,13 @@ class SkiaLayerTest {
window
.
setLocation
(
200
,
200
)
window
.
setLocation
(
200
,
200
)
window
.
setSize
(
400
,
200
)
window
.
setSize
(
400
,
200
)
window
.
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
window
.
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
window
.
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
window
.
layer
.
renderDelegate
=
SkikoRenderDelegate
{
_
,
_
,
_
,
_
->
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
drawCount
++
drawCount
++
if
(
drawCount
<
targetDrawCount
)
{
if
(
drawCount
<
targetDrawCount
)
{
window
.
layer
.
needRender
()
window
.
layer
.
needRender
()
}
else
{
}
else
{
onDrawCompleted
.
complete
(
Unit
)
onDrawCompleted
.
complete
(
Unit
)
}
}
}
}
}
window
.
isUndecorated
=
true
window
.
isUndecorated
=
true
...
@@ -734,12 +718,10 @@ class SkiaLayerTest {
...
@@ -734,12 +718,10 @@ class SkiaLayerTest {
try
{
try
{
window
.
setLocation
(
200
,
200
)
window
.
setLocation
(
200
,
200
)
window
.
setSize
(
400
,
400
)
window
.
setSize
(
400
,
400
)
window
.
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
window
.
layer
.
renderDelegate
=
SkikoRenderDelegate
{
_
,
_
,
_
,
_
->
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
window
.
dispose
()
window
.
dispose
()
SwingUtilities
.
invokeLater
{
SwingUtilities
.
invokeLater
{
onDrawCompleted
.
complete
(
Unit
)
onDrawCompleted
.
complete
(
Unit
)
}
}
}
}
}
window
.
isVisible
=
true
window
.
isVisible
=
true
...
@@ -812,10 +794,8 @@ class SkiaLayerTest {
...
@@ -812,10 +794,8 @@ class SkiaLayerTest {
assumeTrue
(
hostOs
==
OS
.
MacOS
)
// since the test has 'metal' in its name (it is flaky on Windows)
assumeTrue
(
hostOs
==
OS
.
MacOS
)
// since the test has 'metal' in its name (it is flaky on Windows)
val
renderTimes
=
mutableListOf
<
Long
>()
val
renderTimes
=
mutableListOf
<
Long
>()
val
renderer
=
object
:
SkikoRenderDelegate
{
val
renderer
=
SkikoRenderDelegate
{
_
,
_
,
_
,
_
->
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
renderTimes
.
add
(
System
.
currentTimeMillis
())
renderTimes
.
add
(
System
.
currentTimeMillis
())
}
}
}
val
window
=
UiTestWindow
{
val
window
=
UiTestWindow
{
layer
.
renderDelegate
=
renderer
layer
.
renderDelegate
=
renderer
...
@@ -981,12 +961,10 @@ class SkiaLayerTest {
...
@@ -981,12 +961,10 @@ class SkiaLayerTest {
val
paragraph
by
lazy
{
paragraph
(
window
.
layer
.
contentScale
*
40
,
"=-+Нп"
)
}
val
paragraph
by
lazy
{
paragraph
(
window
.
layer
.
contentScale
*
40
,
"=-+Нп"
)
}
window
.
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
window
.
layer
.
renderDelegate
=
SkikoRenderDelegate
{
canvas
,
_
,
_
,
_
->
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
canvas
.
clear
(
Color
.
WHITE
.
rgb
)
canvas
.
clear
(
Color
.
WHITE
.
rgb
)
paragraph
.
layout
(
Float
.
POSITIVE_INFINITY
)
paragraph
.
layout
(
Float
.
POSITIVE_INFINITY
)
paragraph
.
paint
(
canvas
,
0f
,
0f
)
paragraph
.
paint
(
canvas
,
0f
,
0f
)
}
}
}
window
.
isUndecorated
=
true
window
.
isUndecorated
=
true
...
@@ -1036,7 +1014,7 @@ class SkiaLayerTest {
...
@@ -1036,7 +1014,7 @@ class SkiaLayerTest {
// Ideally, layoutCount would be just 1, but Swing appears to call layout one extra time, so it ends up being 2.
// Ideally, layoutCount would be just 1, but Swing appears to call layout one extra time, so it ends up being 2.
// Compare to 3 just to avoid a false-failure if there's another layout for whatever reason.
// Compare to 3 just to avoid a false-failure if there's another layout for whatever reason.
// What we're interested
to validate
is that there's no layout occurring on every window move.
// What we're interested
in validating
is that there's no layout occurring on every window move.
assert
(
layoutCount
<=
3
)
{
assert
(
layoutCount
<=
3
)
{
"Layout count: $layoutCount"
"Layout count: $layoutCount"
}
}
...
@@ -1054,7 +1032,7 @@ class SkiaLayerTest {
...
@@ -1054,7 +1032,7 @@ class SkiaLayerTest {
if
(
renderApi
!=
GraphicsApi
.
METAL
)
return
@uiTest
if
(
renderApi
!=
GraphicsApi
.
METAL
)
return
@uiTest
// Put up a large green window, and then repeatedly show and hide/dispose
// Put up a large green window, and then repeatedly show and hide/dispose
// a smaller black window on top of it while screenshotting the pixel at the center
,
// a smaller black window on top of it while screenshotting the pixel at the center
// and making sure that pixel is always either black or green.
// and making sure that pixel is always either black or green.
val
bgColor
=
Color
.
GREEN
val
bgColor
=
Color
.
GREEN
...
@@ -1195,6 +1173,7 @@ class SkiaLayerTest {
...
@@ -1195,6 +1173,7 @@ class SkiaLayerTest {
val
pixel
=
robot
.
getPixelColor
(
pixelLocation
.
x
,
pixelLocation
.
y
)
val
pixel
=
robot
.
getPixelColor
(
pixelLocation
.
x
,
pixelLocation
.
y
)
if
(
pixel
!=
color
)
{
if
(
pixel
!=
color
)
{
tempColorVisibleCount
++
tempColorVisibleCount
++
break
}
}
}
}
}
}
...
@@ -1235,11 +1214,9 @@ class SkiaLayerTest {
...
@@ -1235,11 +1214,9 @@ class SkiaLayerTest {
val
window
=
UiTestWindow
(
analytics
=
analytics
)
{
val
window
=
UiTestWindow
(
analytics
=
analytics
)
{
size
=
Dimension
(
600
,
600
)
size
=
Dimension
(
600
,
600
)
location
=
Point
(
400
,
400
)
location
=
Point
(
400
,
400
)
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
layer
.
renderDelegate
=
SkikoRenderDelegate
{
_
,
_
,
_
,
_
->
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
renderCalls
++
renderCalls
++
renderChannel
.
trySend
(
Unit
)
renderChannel
.
trySend
(
Unit
)
}
}
}
contentPane
.
add
(
layer
,
BorderLayout
.
CENTER
)
contentPane
.
add
(
layer
,
BorderLayout
.
CENTER
)
}
}
...
@@ -1301,11 +1278,9 @@ class SkiaLayerTest {
...
@@ -1301,11 +1278,9 @@ class SkiaLayerTest {
val
window
=
UiTestWindow
(
analytics
=
analytics
)
{
val
window
=
UiTestWindow
(
analytics
=
analytics
)
{
size
=
Dimension
(
600
,
600
)
size
=
Dimension
(
600
,
600
)
location
=
Point
(
400
,
400
)
location
=
Point
(
400
,
400
)
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
layer
.
renderDelegate
=
SkikoRenderDelegate
{
_
,
_
,
_
,
_
->
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
renderCalls
++
renderCalls
++
renderChannel
.
trySend
(
Unit
)
renderChannel
.
trySend
(
Unit
)
}
}
}
contentPane
.
add
(
layer
,
BorderLayout
.
CENTER
)
contentPane
.
add
(
layer
,
BorderLayout
.
CENTER
)
}
}
...
...
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