Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,6 @@ actual open class SkiaLayer {
if (value) throw IllegalArgumentException("changing fullscreen is unsupported")
}

actual var transparency: Boolean
get() = false
set(value) {
if (value) throw IllegalArgumentException("transparency unsupported")
}

/**
* The background color of the layer.
*/
actual internal var backgroundColor: Int = Color.WHITE
set(value) {
field = value
needRender()
}

actual var renderDelegate: SkikoRenderDelegate? = null

actual fun attachTo(container: Any) {
Expand Down Expand Up @@ -81,8 +66,5 @@ 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()
}
34 changes: 28 additions & 6 deletions skiko/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.jetbrains.skia.*
import org.jetbrains.skiko.context.cutoutFromClip
import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.redrawer.RedrawerManager
import java.awt.Color
Expand Down Expand Up @@ -194,13 +195,20 @@ actual open class SkiaLayer internal constructor(
}

private var _transparency: Boolean = false
actual var transparency: Boolean

/**
* Whether transparency is enabled.
*/
var transparency: Boolean
get() = _transparency
set(value) {
configureBackground(value, _background)
}

internal actual var backgroundColor: Int
/**
* The color, in ARGB format, with which the layer is cleared before rendering.
*/
internal var backgroundColor: Int
get() = background.rgb // Will return an ancestor's non-null background after setBackground(null).
set(value) {
configureBackground(_transparency, Color(value, true))
Expand Down Expand Up @@ -333,11 +341,11 @@ actual open class SkiaLayer internal constructor(
jComponent.add(this)
}

/**
* A list of rectangles to cut out from the rendered content; No content will be drawn inside them.
*/
val clipComponents = mutableListOf<ClipRectangle>()

internal actual val cutoutRectangles: List<ClipRectangle>
get() = clipComponents

@Volatile
private var isDisposed = false

Expand Down Expand Up @@ -603,13 +611,27 @@ actual open class SkiaLayer internal constructor(
// If this approach will be changed, create an issue in https://youtrack.jetbrains.com/issues/CMP for changing it in
// https://github.com/JetBrains/compose-multiplatform/blob/e4e2d329709cded91a09cc612d4defbce37aad96/benchmarks/multiplatform/benchmarks/src/commonMain/kotlin/MeasureComposable.kt#L151 as well

val contentScale = this.contentScale
val pictureWidth = (backedLayer.width * contentScale).coerceAtLeast(0f)
val pictureHeight = (backedLayer.height * contentScale).coerceAtLeast(0f)
val intWidth = pictureWidth.toInt()
val intHeight = pictureHeight.toInt()

val pictureRecorder = pictureRecorder!!
val canvas = pictureRecorder.beginRecording(0f, 0f, pictureWidth, pictureHeight)
val canvas = pictureRecorder.beginRecording(0f, 0f, pictureWidth, pictureHeight).apply {
for (clip in clipComponents) {
cutoutFromClip(clip, contentScale)
}

val layerBg = backgroundColor
clear(
if (transparency && (redrawer?.isTransparentBackgroundSupported() == true)) {
layerBg
} else {
layerBg or 0xFF000000.toInt()
}
)
}

try {
isRendering = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ import java.awt.color.ColorSpace
import java.awt.image.*

internal class SoftwareContextHandler(layer: SkiaLayer) : ContextFreeContextHandler(layer) {
override fun isTransparentBackgroundSupported(): Boolean {
// TODO: why Software rendering has another transparency logic from the beginning
return hostOs == OS.MacOS
}

val colorModel = ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,6 @@ internal abstract class AWTRedrawer(
protected fun checkDisposed() {
check(!isDisposed) { "${this.javaClass.simpleName} is disposed" }
}

override fun isTransparentBackgroundSupported() = defaultIsTransparentBackgroundSupported(layer)
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,9 @@ internal class SoftwareRedrawer(
}
}
}

override fun isTransparentBackgroundSupported(): Boolean {
// TODO: why Software rendering has another transparency logic from the beginning
return hostOs == OS.MacOS
}
}
2 changes: 2 additions & 0 deletions skiko/src/awtTest/kotlin/org/jetbrains/skiko/SkiaLayerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.jetbrains.skiko.context.JvmContextHandler
import org.jetbrains.skiko.redrawer.MetalRedrawer
import org.jetbrains.skiko.redrawer.MetalVSyncer
import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.redrawer.defaultIsTransparentBackgroundSupported
import org.jetbrains.skiko.swing.SkiaSwingLayer
import org.jetbrains.skiko.util.ScreenshotTestRule
import org.jetbrains.skiko.util.UiTestScope
Expand Down Expand Up @@ -565,6 +566,7 @@ class SkiaLayerTest {
override fun needRender(throttledToVsync: Boolean) = frameDispatcher.scheduleFrame()
override fun renderImmediately() = Unit
override fun update(nanoTime: Long) = layer.update(nanoTime)
override fun isTransparentBackgroundSupported() = defaultIsTransparentBackgroundSupported(layer)

override val renderInfo: String
get() = ""
Expand Down
15 changes: 0 additions & 15 deletions skiko/src/commonMain/kotlin/org/jetbrains/skiko/SkiaLayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,11 @@ expect open class SkiaLayer {
*/
var fullscreen: Boolean

/**
* If transparency is enabled.
*/
var transparency: Boolean

/**
* The color, in ARGB format, with which the layer is cleared before rendering.
*/
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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,35 +42,10 @@ internal abstract class ContextHandler(
initCanvas()
canvas?.runRestoringState {
clear(Color.TRANSPARENT)

val scale = layer.contentScale
for (clip in layer.cutoutRectangles) {
cutoutFromClip(clip, scale)
}

val layerBg = layer.backgroundColor
clear(
if (layer.transparency && isTransparentBackgroundSupported()) {
layerBg
} else {
layerBg or 0xFF000000.toInt()
}
)

drawContent()
}
flush()
}

protected open fun isTransparentBackgroundSupported(): Boolean {
if (hostOs == OS.MacOS) {
// macOS transparency is always supported
return true
}

// for non-macOS in fullscreen transparency is not supported
return !layer.fullscreen
}
}

@Suppress("NOTHING_TO_INLINE")
Expand All @@ -85,7 +60,7 @@ internal inline fun Canvas.cutoutFromClip(rectangle: ClipRectangle, scale: Float
)
}

private inline fun Canvas.runRestoringState(block: Canvas.() -> Unit) {
internal inline fun Canvas.runRestoringState(block: Canvas.() -> Unit) {
val restoreCount = save()
try {
block()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.jetbrains.skiko.redrawer

import org.jetbrains.skiko.OS
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.hostOs
import kotlin.time.TimeSource

private val initialTime = TimeSource.Monotonic.markNow()
Expand All @@ -12,4 +15,15 @@ internal interface Redrawer {
fun update(nanoTime: Long = initialTime.elapsedNow().inWholeNanoseconds)
fun setVisible(isVisible: Boolean) = Unit
val renderInfo: String
fun isTransparentBackgroundSupported(): Boolean
}

internal fun defaultIsTransparentBackgroundSupported(layer: SkiaLayer): Boolean {
if (hostOs == OS.MacOS) {
// macOS transparency is always supported
return true
}

// for non-macOS in fullscreen transparency is not supported
return !layer.fullscreen
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,8 @@ actual open class SkiaLayer {
actual var fullscreen: Boolean
get() = TODO("Not yet implemented")
set(value) {}
actual var transparency: Boolean
get() = TODO("Not yet implemented")
set(value) {}
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")
}
Expand Down
25 changes: 3 additions & 22 deletions skiko/src/macosMain/kotlin/org/jetbrains/skiko/SkiaLayer.macos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,6 @@ actual open class SkiaLayer {
if (value) throw IllegalArgumentException("fullscreen unsupported")
}

/**
* Transparency is not supported on macOS native.
*/
actual var transparency: Boolean
get() = false
set(value) {
if (value) throw IllegalArgumentException("transparency unsupported")
}

/**
* The background color of the layer.
*/
internal actual var backgroundColor: Int = Color.WHITE
set(value) {
field = value
needRender()
}

/**
* Underlying [NSView]
*/
Expand All @@ -76,9 +58,6 @@ 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.
*/
Expand Down Expand Up @@ -172,7 +151,9 @@ actual open class SkiaLayer {
val pictureWidth = (width * contentScale).coerceAtLeast(0.0)
val pictureHeight = (height * contentScale).coerceAtLeast(0.0)

val canvas = pictureRecorder.beginRecording(0f, 0f, pictureWidth.toFloat(), pictureHeight.toFloat())
val canvas = pictureRecorder.beginRecording(0f, 0f, pictureWidth.toFloat(), pictureHeight.toFloat()).apply {
clear(Color.WHITE)
}
renderDelegate?.onRender(canvas, pictureWidth.toInt(), pictureHeight.toInt(), nanoTime)

val picture = pictureRecorder.finishRecordingAsPicture()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ internal class MacOsMetalRedrawer(
}
}
}

override fun isTransparentBackgroundSupported() = defaultIsTransparentBackgroundSupported(skiaLayer)
}

internal class MetalLayer : CAMetalLayer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ internal class MacOsOpenGLRedrawer(
glLayer.setNeedsDisplay()
skiaLayer.nsView.setNeedsDisplay(true)
}

override fun isTransparentBackgroundSupported() = defaultIsTransparentBackgroundSupported(skiaLayer)
}

internal class MacosGLLayer : CAOpenGLLayer {
Expand Down
20 changes: 3 additions & 17 deletions skiko/src/uikitMain/kotlin/org/jetbrains/skiko/SkiaLayer.uikit.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,6 @@ actual open class SkiaLayer {
get() = true
set(_) { throw UnsupportedOperationException() }

actual var transparency: Boolean
get() = false
set(_) { throw UnsupportedOperationException() }

/**
* The background color of the layer, as transparency is not supported.
*/
internal actual var backgroundColor: Int = Color.WHITE
set(value) {
field = value
needRender()
}

actual fun needRender(throttledToVsync: Boolean) {
needRedrawCallback.invoke()
}
Expand All @@ -46,9 +33,6 @@ 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()
Expand Down Expand Up @@ -80,7 +64,9 @@ actual open class SkiaLayer {
}

internal fun draw(surface: Surface) {
renderDelegate?.onRender(surface.canvas, surface.width, surface.height, currentNanoTime())
val canvas = surface.canvas
canvas.clear(Color.WHITE)
renderDelegate?.onRender(canvas, surface.width, surface.height, currentNanoTime())
}

actual val pixelGeometry: PixelGeometry
Expand Down
Loading
Loading